![](/img/trans.png)
[英]How to use SignalR events to keep connection alive in the right way?
[英]How to handle updates in the right way using signalr?
我有一個客戶端應用程序Angular
和一個signalR
集線器,還有一個將時間戳作為參數的服務。
當我按下客戶端中的開始按鈕時,我想調用集線器中的一個方法,當調用該方法時,我想繼續列出所有更改(創建一個計時器),直到客戶端按下停止按鈕,然后我將停止計時器。
所以我想問一下哪個更好:
1- 使用時間戳從客戶端調用被調用的方法,然后創建一個setInterval
來調用其中的方法,當按下停止按鈕時,我可以停止它。
優點:啟動和停止計時器很容易。
缺點:我每 1 秒調用一次該方法,然后檢查客戶端是否有更新 UI 的響應。
2- 調用一次該方法,然后為服務器上的每個客戶端創建一個計時器,當客戶端按下停止按鈕時,我可以調用另一種方法來停止該客戶端的計時器。
優點:我正在檢查集線器中的時間戳,只有當來自服務的時間戳 > 本地時間戳時,我才會將數據發送到客戶端
缺點:我實際上不知道如何為每個客戶端創建一個計時器,所以如果這是正確的方法,請幫助我
您正在使用SignalR
進行實時數據通信。 每秒調用一個方法只是在SignalR
臉上開玩笑......所以這不是解決方案。
最好的解決方案是使用組功能。
例子:
await this.Clients.Group("someGroup").BroadcastMessage(message);
集線器上的一些代碼示例:
public async Task Start()
{
// Add user to the data group
await this.Groups.AddToGroupAsync(this.Context.ConnectionId, "dataGroup");
}
public async Task Stop()
{
// Add user to the data group
await this.Groups.RemoveFromGroupAsync(this.Context.ConnectionId, "dataGroup");
}
向按下啟動鍵的用戶發送數據並接收實時數據的 Worker 示例。
private readonly IHubContext<SignalRHub, ISignalRHub> hub;
private readonly IServiceProvider serviceProvider;
public Worker(IServiceProvider serviceProvider, IHubContext<SignalRHub, ISignalRHub> hub)
{
this.serviceProvider = serviceProvider;
this.hub = hub;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await this.hub.Clients.Group("dataGroup").BroadcastMessage(DataManager.GetData());
this.Logger.LogDebug("Sent data to all users at {0}", DateTime.UtcNow);
await Task.Delay(1000, stoppingToken);
}
}
PS:如果你有工人,我假設你有一些經理來獲取數據或要發送給用戶的東西。
編輯:如果你不想用戶工作者,你可以總是像這樣的計時器:
public class TimerManager
{
private Timer _timer;
private AutoResetEvent _autoResetEvent;
private Action _action;
public DateTime TimerStarted { get; }
public TimerManager(Action action)
{
_action = action;
_autoResetEvent = new AutoResetEvent(false);
_timer = new Timer(Execute, _autoResetEvent, 1000, 2000);
TimerStarted = DateTime.Now;
}
public void Execute(object stateInfo)
{
_action();
if((DateTime.Now - TimerStarted).Seconds > 60)
{
_timer.Dispose();
}
}
}
然后在某處使用它,例如:
var timerManager = new TimerManager(() => this.hub.Clients.Group("dataGroup").BroadcastMessage(DataManager.GetData()));
選項 #1 不可用,因為 SignalR 的存在是為了消除輪詢的需要。 頻繁的輪詢也不能擴展。 如果每個客戶端每 1 秒輪詢一次服務器,該網站最終將白白付出大量CPU 和帶寬。 商務人士也不喜歡頻繁輪詢,因為所有托管商和雲提供商都對出口收費。
SignalR 流示例使用定時通知作為使用IAsyncEnumerable<T>
的流通知的簡單示例。 在最簡單的例子中,計數器每delay
毫秒遞增一次:
public class AsyncEnumerableHub : Hub
{
public async IAsyncEnumerable<int> Counter(
int count,
int delay,
[EnumeratorCancellation]
CancellationToken cancellationToken)
{
for (var i = 0; i < count; i++)
{
// Check the cancellation token regularly so that the server will stop
// producing items if the client disconnects.
cancellationToken.ThrowIfCancellationRequested();
yield return i;
// Use the cancellationToken in other APIs that accept cancellation
// tokens so the cancellation can flow down to them.
await Task.Delay(delay, cancellationToken);
}
}
}
客戶端可以調用此操作傳遞所需的延遲並開始接收通知。 SignalR 知道這是一個通知流,因為它返回IAsyncEnumerable
。
下一個更高級的示例使用Channels來允許發布者方法WriteItemsAsync
向中心發送通知流。
動作本身更簡單,它只是返回頻道的閱讀器:
public ChannelReader<int> Counter(
int count,
int delay,
CancellationToken cancellationToken)
{
var channel = Channel.CreateUnbounded<int>();
// We don't want to await WriteItemsAsync, otherwise we'd end up waiting
// for all the items to be written before returning the channel back to
// the client.
_ = WriteItemsAsync(channel.Writer, count, delay, cancellationToken);
return channel.Reader;
}
發布者方法寫入ChannelWriter
而不是返回IAsyncEnumerable
:
private async Task WriteItemsAsync(
ChannelWriter<int> writer,
int count,
int delay,
CancellationToken cancellationToken)
{
Exception localException = null;
try
{
for (var i = 0; i < count; i++)
{
await writer.WriteAsync(i, cancellationToken);
// Use the cancellationToken in other APIs that accept cancellation
// tokens so the cancellation can flow down to them.
await Task.Delay(delay, cancellationToken);
}
}
catch (Exception ex)
{
localException = ex;
}
writer.Complete(localException);
}
這個方法可以很容易地在不同的類中。 所需要做的就是將ChannelWriter
傳遞給發布者。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.