У меня архитектурная проблема, для которой я просто не могу найти подходящего решения.В моем веб-приложении на основе ASP.NET Core MVC 2.2 я хочу получить данные из защищенного JWT API и опубликовать их на подключенных клиентах с помощью SignalR.Кроме того, данные должны выбираться только в том случае, если хотя бы один клиент действительно подключен к концентратору SignalR.Моя проблема: я не могу найти способ отменить Task.Delay
внутри цикла while, когда подключен дополнительный клиент.Чтобы пояснить, позвольте мне показать вам, что я придумал.
Прежде всего, вот класс клиента API:
public class DataApiClient : IDataApiClient {
private readonly HttpClient httpClient;
private readonly string dataApiDataUrl;
public DataApiClient(HttpClient httpClient, IOptionsMonitor<DataSettings> dataSettings) {
this.httpClient = httpClient;
dataApiUrl = dataSettings.CurrentValue.dataApiUrl;
}
public async Task<DataOverview> GetData(string accessToken) {
DataOverview dataOverview = new DataOverview();
try {
httpClient.DefaultRequestHeaders.Accept.Clear();
// more httpClient setup
Task<Stream> streamTask = httpClient.GetStreamAsync(dataApiUrl);
dataOverview = serializer.ReadObject(await streamTask) as DataOverview;
} catch(Exception e) {
Debug.WriteLine(e.Message);
}
return dataOverview;
}
}
Концентратор SignalR:
public interface IDataClient {
Task ReceiveData(DataOverview dataOverview);
}
public class DataHub : Hub<IDataClient> {
private volatile static int UserCount = 0;
public static bool UsersConnected() {
return UserCount > 0;
}
public override Task OnConnectedAsync() {
Interlocked.Increment(ref UserCount);
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception) {
Interlocked.Decrement(ref UserCount);
return base.OnDisconnectedAsync(exception);
}
}
И BackgroundService
, который выполняет работу:
public class DataService : BackgroundService {
private readonly IHubContext<DataHub, IDataClient> hubContext;
private readonly IDataApiClient dataApiClient;
private readonly IAccessTokenGenerator accessTokenGenerator;
private AccessToken accessToken;
public DataService(IHubContext<DataHub, IDataClient> hubContext, IDataApiClient dataApiClient, IAccessTokenGenerator accessTokenGenerator) {
this.hubContext = hubContext;
this.dataApiClient = dataApiClient;
this.accessTokenGenerator = accessTokenGenerator;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
while (!stoppingToken.IsCancellationRequested) {
if (DataHub.UsersConnected()) {
if (NewAccessTokenNeeded()) {
accessToken = accessTokenGenerator.GetAccessToken();
}
DataOverview dataOverview = dataApiClient.GetData(accessToken.Token).Result;
dataOverview.LastUpdated = DateTime.Now.ToString();
await hubContext.Clients.All.ReceiveData(dataOverview);
}
// how to cancel this delay, as soon as an additional user connects to the hub?
await Task.Delay(60000);
}
}
private bool NewAccessTokenNeeded() {
return accessToken == null || accessToken.ExpireDate < DateTime.UtcNow;
}
}
Итак, мои вопросы:
Моя главная проблема: как я могу отменить Task.Delay()
внутри ExecuteAsync()
цикла while в тот момент, когда подключается дополнительный пользователь, поэтому вновь подключенный клиент получает данные немедленно и ему не нужно ждать, пока задача Delay()
закончится?Я думаю, это должно быть помещено в OnConnectedAsync()
, но вызов службы оттуда, кажется, не является хорошим решением.
Является ли эта архитектура даже хорошей?Если нет, то как бы вы реализовали такой сценарий?
Есть ли лучший способ сохранить количество подключенных в данный момент пользователей SignalR?Я читал, что статическое свойство в Hub
может быть проблематичным, если задействовано более одного сервера SignalR (но это не так для меня).
Имеет IHostedService
/BackgroundService
здесь вообще есть смысл?Так как сервисы, добавляемые с помощью AddHostedService()
, теперь являются временными, не нарушает ли цикл while внутри метода ExecuteAsync()
цель этого подхода?
Что было бы лучшим местомхранить токен, такой как токены доступа JWT, чтобы временные экземпляры могли получить к нему доступ, если он действителен, и обновить его по истечении срока действия?
Я также читал о , добавляющем ссылку наконкретный IHostedService
, но это, кажется, просто неправильно.Также эта дискуссия о Github дала мне понять, что должен быть лучший способ создать связь между SignalR и постоянно работающими сервисами.
Любая помощь будет принята с благодарностью.