У меня архитектурная проблема, для которой я просто не могу найти подходящего решения. В моем веб-приложении, основанном на ASP.NET Core MVC 2.2, я хочу извлекать данные из защищенного API JWT и публиковать их для подключенных клиентов с помощью 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 (но это не для меня).Имеет ли здесь смысл _11 _ / _ 12_? Поскольку службы, добавляемые с использованием
AddHostedService()
, теперь являются временными, разве цикл while внутри методаExecuteAsync()
не противоречит цели этого подхода?В каком месте лучше всего хранить токен, например токены доступа JWT, чтобы временные экземпляры могли получить к нему доступ, если он действителен, и обновить его по истечении срока его действия?
Я также читал о о введении ссылки на конкретный IHostedService
< / a>, но это кажется неправильным. Кроме того, это обсуждение на Github заставило меня почувствовать, что должен быть лучший способ разработки связь между SignalR и постоянно работающими службами.
Любая помощь будет принята с благодарностью.