我遇到了体系结构问题,只是找不到适合的解决方案。在基于ASP.NET Core MVC 2.2的Web应用程序中,我想从JWT安全的API中提取数据并使用SignalR将其发布到连接的客户端。此外,仅当至少一个客户端实际连接到SignalR集线器时,才应提取数据。我的问题:当连接其他客户端时,我找不到在while循环中取消Task.Delay
的方法。为澄清起见,让我向您展示我到目前为止的想法。
首先,这是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()
循环,因此新连接的客户端可以立即获取数据,而不必等到Delay()
任务结束吗?我想这应该放在OnConnectedAsync()
中,但是从那里调用服务似乎不是一个好的解决方案。
这个体系结构还不错吗?如果没有,您将如何实现这种情况?
是否有更好的方法来统计当前连接的SignalR用户?我读到Hub
中的静态属性可能会出现问题,如果涉及多个SignalR服务器(但这对我来说不是这种情况)。
IHostedService
/ BackgroundService
在这里是否还有意义?由于使用AddHostedService()
添加的服务现在是临时的,因此ExecuteAsync()
方法内的while循环是否会违反这种方法的目的?
存储令牌(例如JWT访问令牌)的最佳位置是什么,以便临时实例可以访问它(如果有效)并在过期时对其进行更新?
我也读过injecting a reference to a specific IHostedService
,但这似乎是错误的。 this discussion on Github也让我感到必须有一种更好的方法来设计SignalR和连续运行的服务之间的通信。
任何帮助将不胜感激。