Azure SignalR 服务中的复原能力和灾难恢复

复原能力和灾难恢复是联机系统的常见需求。 Azure SignalR 服务已提供 99.9% 的可用性,但它仍然是一个区域性的服务。 由于你的服务实例始终在一个区域中运行,因此在出现区域范围的服务中断时不会故障转移到另一个区域。

对于区域灾难恢复,建议采用以下两种方法:

  • 启用异地复制(简便方法)。 此功能将自动处理区域故障转移。 启用后,将仅保留一个 Azure SignalR 实例,并且不会引入任何代码更改。 有关详细信息,请查看异地复制
  • 利用 Service SDK 中的多个终结点。 我们的服务 SDK 支持多个 SignalR 服务实例,并在其中一些实例不可用时自动切换到其他实例。 发生灾难时,可以使用此功能进行恢复,但仍需要自行设置正确的系统拓扑。 本文档将介绍此操作。

SignalR 服务的高可用性体系结构

若要确保 SignalR 服务的跨区域复原能力,需要在不同的区域中设置多个服务实例。 这样,当某个区域出现故障时,可将其他区域用作备用区域。 当应用服务器连接到多个服务实例时,有两个角色:主要角色和辅助角色。 主要实例负责接收联机流量,而辅助实例充当完全正常运行的回退实例。 在 SDK 实现中,协商仅返回主要终结点,因此在正常情况下客户端只连接到主要终结点。 但当主要实例出现故障时,协商会返回辅助终结点,因此客户端仍可建立连接。 主要实例和应用服务器通过正常的服务器连接进行连接,但辅助实例和应用服务器通过一种称作“弱连接”的特殊连接进行连接。 弱连接的一个不同特征是它无法接受客户端连接路由,因为辅助实例位于另一个区域中。 将客户端路由到另一个区域不是最佳选择(会增大延迟)。

一个服务实例在连接到多个应用服务器时可以有不同的角色。 跨区域方案的一种典型设置是使用两对或更多对 SignalR 服务实例和应用服务器。 在每一对中,应用服务器和 SignalR 服务位于同一区域,SignalR 服务作为主要角色连接到应用服务器。 在每对之间,应用服务器和 SignalR 服务也会建立连接,但是,在连接到另一区域中的服务器时,SignalR 将变成辅助角色。

使用此拓扑时,来自一台服务器的消息仍可传送到所有客户端,因为所有应用服务器和 SignalR 服务实例是互连的。 但是,客户端在连接后,会路由到同一区域中的应用服务器,以实现最佳网络延迟。

下图阐释了这种方案:

Diagram shows two regions each with an app server and a SignalR service, where each server is associated with the SignalR service in its region as primary and with the service in the other region as secondary.

配置多个 SignalR 服务实例

应用服务器和 Azure Functions 都支持多个 SignalR 服务实例。

在每个区域中创建 SignalR 服务和应用服务器/Azure Functions 后,可将应用服务器/Azure Functions 配置为连接到所有 SignalR 服务实例。

在应用服务器上配置

可通过两种方式实现此目的:

通过配置

你应该已经知道如何通过环境变量/应用设置/web.cofig 在名为 Azure:SignalR:ConnectionString 的配置项中设置 SignalR 服务连接字符串。 如果有多个终结点,可在多个配置项中设置这些终结点,每个项采用以下格式:

Azure:SignalR:ConnectionString:<name>:<role>

在 ConnectionString 中,<name> 是终结点的名称,<role> 是其角色(主要或辅助角色)。 名称是可选的,但如果你想要进一步自定义多个终结点之间的路由行为,则名称非常有用。

通过代码

如果你偏向于将连接字符串存储到其他位置,则也可以在代码中读取连接字符串,并在调用 AddAzureSignalR()(在 ASP.NET Core 中)或 MapAzureSignalR()(在 ASP.NET 中)时将其用作参数。

下面是示例代码:

ASP.NET Core:

services.AddSignalR()
        .AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[]
        {
            new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
            new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
        });

ASP.NET:

app.MapAzureSignalR(GetType().FullName, hub,  options => options.Endpoints = new ServiceEndpoint[]
    {
        new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
        new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
    };

可以配置多个主要或次要实例。 如果有多个主要实例和/或次要实例,则协商会按以下顺序返回终结点:

  1. 如果有至少一个主要实例处于联机状态,则会返回一个随机的联机主要实例。
  2. 如果所有主要实例都停机,则会返回一个随机的联机次要实例。

在 Azure Functions 上配置

请参阅此文

故障转移序列和最佳做法

现已设置正确的系统拓扑。 每当某个 SignalR 服务实例出现故障时,联机流量都会路由到其他实例。 下面是当主要实例出现故障时(以及在一段时间后进行恢复时)发生的情况:

  1. 主要服务实例出现故障,此实例上的所有服务器连接都会断开。
  2. 连接到此实例的所有服务器都会将此实例标记为脱机,协商会停止返回此终结点,开始返回辅助终结点。
  3. 此实例上的所有客户端连接也会关闭,然后客户端会重新进行连接。 由于应用服务器现在返回辅助终结点,因此客户端会连接到辅助实例。
  4. 现在,辅助实例将接收所有联机流量。 由于辅助实例已连接到所有应用服务器,因此从服务器发往客户端的所有消息仍可传送。 但是,从客户端发往服务器的消息只能路由到同一区域中的应用服务器。
  5. 主要实例恢复并重新联机后,应用服务器将与它重新建立连接,并将其标记为联机。 协商现在会再次返回主要终结点,因此,新客户端会重新连接到主要实例。 但现有客户端不会掉线,在断开自身连接之前仍会路由到辅助客户端。

下图演示了 SignalR 服务中如何实现故障转移:

图 1:故障转移之前 Before Failover

图 2:故障转移之后After Failover

图 3 主实例恢复后的短时间内Short time after primary recovers

可以看到,在正常情况下,只有主要应用服务器和 SignalR 服务包含联机流量(以蓝色表示)。 故障转移后,辅助应用服务器和 SignalR 服务也处于活动状态。 主要 SignalR 服务重新联机后,新客户端将连接到主要 SignalR。 但是,现有客户端仍连接到辅助实例,因此这两个实例都包含流量。 所有现有客户端断开连接后,系统将会恢复正常(图 1)。

可以使用两种主要模式来实现跨区域的高可用性体系结构:

  1. 第一种模式是使用一对应用服务器和 SignalR 服务实例来接收所有联机流量,并使用另一对作为备用实例(称为主动/被动配置,如图 1 所示)。
  2. 另一种模式是使用两对(或更多对)应用服务器和 SignalR 服务实例,其中每个实例接收一部分联机流量,并充当其他对的备用实例(称为主动/主动配置,类似于图 3)。

SignalR 服务支持这两种模式,主要差别在于实现应用服务器的方式。 如果应用服务器采用主动/被动配置,则 SignalR 服务也采用主动/被动配置(因为主要应用服务器仅返回其主要 SignalR 服务实例)。 如果应用服务器采用主动/主动配置,则 SignalR 服务也采用主动/主动配置(因为所有应用服务器都将返回其自己的主要 SignalR 实例,因此它们都可以获得流量)。

请注意,无论选择使用什么模式,都需要将每个 SignalR 服务实例作为主要实例连接到应用服务器。

另外,由于 SignalR 连接的性质(远距离连接),发生灾难和故障转移时,客户端会遇到连接断开的情况。 需要在客户端上处理此类情况,使其对最终客户透明。 例如,关闭连接后不要重新连接。

如何测试故障转移

按照以下步骤触发故障转移:

  1. 在门户中主要资源的“网络”选项卡中,禁用公用网络访问。 如果资源已启用专用网络,请使用访问控制规则来拒绝所有流量。
  2. 重启主要资源。

后续步骤

在本文中,你已了解如何配置应用程序以实现 SignalR 服务的复原能力。 若要更详细地了解 SignalR 服务中的服务器/客户端连接和连接路由,请阅读此文,其中介绍了 SignalR 服务的内部情况。

对于使用多个实例一起处理大量连接的缩放方案(例如分片),请阅读如何缩放多个实例

有关如何使用多个 SignalR 服务实例配置 Azure Functions 的详细信息,请阅读 Azure Functions 中的多个 Azure SignalR 服务实例支持