Как обмениваться информацией между концентратором SignalR и BackgroundService в ASP.NET Core MVC 2.2

У меня архитектурная проблема, для которой я просто не могу найти подходящего решения. В моем веб-приложении, основанном на 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;
    }
}

Итак, мои вопросы:

  1. Моя основная проблема: как я могу отменить Task.Delay() внутри цикла ExecuteAsync() while в тот момент, когда подключается дополнительный пользователь, чтобы новый подключенный клиент немедленно получил данные и не должен был ждать завершения задачи Delay()? Я предполагаю, что это должно быть помещено в OnConnectedAsync(), но вызов службы оттуда не кажется хорошим решением.

  2. Эта архитектура вообще хороша? Если нет, как бы вы реализовали такой сценарий?

  3. Есть ли лучший способ вести учет подключенных в настоящее время пользователей SignalR? Я читал, что статическое свойство в Hub может быть проблематичным, если задействовано более одного сервера SignalR (но это не для меня).

  4. Имеет ли здесь смысл _11 _ / _ 12_? Поскольку службы, добавляемые с использованием AddHostedService(), теперь являются временными, разве цикл while внутри метода ExecuteAsync() не противоречит цели этого подхода?

  5. В каком месте лучше всего хранить токен, например токены доступа JWT, чтобы временные экземпляры могли получить к нему доступ, если он действителен, и обновить его по истечении срока его действия?

Я также читал о о введении ссылки на конкретный IHostedService < / a>, но это кажется неправильным. Кроме того, это обсуждение на Github заставило меня почувствовать, что должен быть лучший способ разработки связь между SignalR и постоянно работающими службами.

Любая помощь будет принята с благодарностью.


person jonas2k    schedule 02.04.2019    source источник