当Azure SignalR 服务配置为 Default 模式时,Azure SignalR 服务提供两种正常关闭 SignalR 中心服务器的模式,Azure SignalR 服务充当 SignalR 客户端和 SignalR 中心服务器之间的代理。
使用此功能的主要优点是防止客户出现意外的连接下降。
相反,可以等待客户端连接在业务逻辑方面自行关闭,甚至可以将客户端连接迁移到另一台服务器,而不会丢失数据。
工作原理
一般情况下,正常关闭过程中将有四个阶段:
将服务器设置为脱机状态
这意味着不会再将客户端连接路由到此服务器。
触发
OnShutdown钩子可以为服务器中拥有的每个集线器注册关闭挂钩。 收到来自Azure SignalR 服务的 FINACK 响应后,我们将按照注册顺序立即调用,这表明该服务器已在Azure SignalR 服务中设为脱机。
可以在此阶段广播消息和执行一些清理作业,一旦所有关闭钩子都已执行,我们将转到下一阶段。
等待所有客户端连接完成,具体取决于所选模式,可以是:
模式设置为 WaitForClientsToClose
Azure SignalR 服务将保留现有客户端。
可能需要设计一种方法,例如向所有客户端广播关闭消息,然后让客户端决定何时关闭/重新连接自身。
读取 ChatSample 以获取示例用法,我们在关闭钩子中广播“退出”消息以触发客户端关闭。
模式设置为 MigrateClients
Azure SignalR 服务将尝试将此服务器上的客户端连接重新路由到另一个有效服务器。
在此方案中,
OnConnectedAsync和OnDisconnectedAsync将分别在新服务器和旧服务器上触发,并且在IConnectionMigrationFeature中设置Context,这可以用于识别客户端连接是迁入还是迁出。此功能特别适用于有状态的方案。在传递当前消息后,客户端连接将立即迁移,这意味着下一条消息将路由到新服务器。
停止服务器连接
所有客户端连接关闭或迁移后,或者超时(默认为 30 秒)后,
SignalR 服务器 SDK 将关闭过程转到此阶段,并关闭所有服务器连接。
如果客户端连接无法关闭/迁移,客户端连接仍将被删除。 例如,没有合适的目标服务器可用/当前从客户端到服务器的消息尚未完成。
示例代码。
在AddAzureSignalR时添加以下选项:
services.AddSignalR().AddAzureSignalR(option =>
{
option.GracefulShutdown.Mode = GracefulShutdownMode.WaitForClientsClose;
// option.GracefulShutdown.Mode = GracefulShutdownMode.MigrateClients;
option.GracefulShutdown.Timeout = TimeSpan.FromSeconds(30);
option.GracefulShutdown.Add<Chat>(async (c) =>
{
await c.Clients.All.SendAsync("exit");
});
});
配置OnConnected并OnDisconnected,同时将正常关闭模式设置为MigrateClients。
我们引入了“IConnectionMigrationFeature”,用于指示连接是否已迁移/移出。
public class Chat : Hub {
public override async Task OnConnectedAsync()
{
Console.WriteLine($"{Context.ConnectionId} connected.");
var feature = Context.Features.Get<IConnectionMigrationFeature>();
if (feature != null)
{
Console.WriteLine($"[{feature.MigrateTo}] {Context.ConnectionId} is migrated from {feature.MigrateFrom}.");
// Your business logic.
}
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception e)
{
Console.WriteLine($"{Context.ConnectionId} disconnected.");
var feature = Context.Features.Get<IConnectionMigrationFeature>();
if (feature != null)
{
Console.WriteLine($"[{feature.MigrateFrom}] {Context.ConnectionId} will be migrated to {feature.MigrateTo}.");
// Your business logic.
}
await base.OnDisconnectedAsync(e);
}
}