使用多個實例調整 SignalR 服務

SignalR Service SDK 支援 SignalR 服務實例的多個端點。 您可以使用此功能來調整並行連線,或使用此功能進行跨區域傳訊。

針對 ASP.NET Core

從組態新增多個端點

使用金鑰 Azure:SignalR:ConnectionStringAzure:SignalR:ConnectionString: SignalR Service 連接字串進行設定。

如果機碼開頭 Azure:SignalR:ConnectionString: 為 ,則其格式 Azure:SignalR:ConnectionString:{Name}:{EndpointType} 應該是 ,其中 NameEndpointType 是 物件的屬性 ServiceEndpoint ,而且可從程式碼存取。

您可以使用下列 dotnet 命令來新增多個實例連接字串:

dotnet user-secrets set Azure:SignalR:ConnectionString:east-region-a <ConnectionString1>
dotnet user-secrets set Azure:SignalR:ConnectionString:east-region-b:primary <ConnectionString2>
dotnet user-secrets set Azure:SignalR:ConnectionString:backup:secondary <ConnectionString3>

從程式碼新增多個端點

類別 ServicEndpoint 描述 Azure SignalR 服務端點的屬性。 使用 Azure SignalR Service SDK 時,您可以透過下列方式設定多個實例端點:

services.AddSignalR()
        .AddAzureSignalR(options => 
        {
            options.Endpoints = new ServiceEndpoint[]
            {
                // Note: this is just a demonstration of how to set options.Endpoints
                // Having ConnectionStrings explicitly set inside the code is not encouraged
                // You can fetch it from a safe place such as Azure KeyVault
                new ServiceEndpoint("<ConnectionString0>"),
                new ServiceEndpoint("<ConnectionString1>", type: EndpointType.Primary, name: "east-region-a"),
                new ServiceEndpoint("<ConnectionString2>", type: EndpointType.Primary, name: "east-region-b"),
                new ServiceEndpoint("<ConnectionString3>", type: EndpointType.Secondary, name: "backup"),
            };
        });

自訂端點路由器

根據預設,SDK 會使用 DefaultEndpointRouter 來挑選端點。

預設行為

  1. 用戶端要求路由:

    使用應用程式伺服器的用戶端 /negotiate 時。 根據預設,SDK 會隨機從一組可用的服務端點中選取 一個端點。

  2. 伺服器訊息路由:

    將訊息傳送至特定 連線 ,並將目標連線路由傳送至目前伺服器時,訊息會直接傳送至該連線的端點。 否則,訊息會廣播至每個 Azure SignalR 端點。

自訂路由演算法

當您有特殊知識可識別訊息應前往的端點時,您可以建立自己的路由器。

下列範例會定義自訂路由器,以從 開頭 east- 的群組將訊息路由傳送至名為 east 的端點:

private class CustomRouter : EndpointRouterDecorator
{
    public override IEnumerable<ServiceEndpoint> GetEndpointsForGroup(string groupName, IEnumerable<ServiceEndpoint> endpoints)
    {
        // Override the group broadcast behavior, if the group name starts with "east-", only send messages to endpoints inside east
        if (groupName.StartsWith("east-"))
        {
            return endpoints.Where(e => e.Name.StartsWith("east-"));
        }

        return base.GetEndpointsForGroup(groupName, endpoints);
    }
}

下列範例會覆寫預設交涉行為,並根據應用程式伺服器的位置選取端點。

private class CustomRouter : EndpointRouterDecorator
{    public override ServiceEndpoint GetNegotiateEndpoint(HttpContext context, IEnumerable<ServiceEndpoint> endpoints)
    {
        // Override the negotiate behavior to get the endpoint from query string
        var endpointName = context.Request.Query["endpoint"];
        if (endpointName.Count == 0)
        {
            context.Response.StatusCode = 400;
            var response = Encoding.UTF8.GetBytes("Invalid request");
            context.Response.Body.Write(response, 0, response.Length);
            return null;
        }

        return endpoints.FirstOrDefault(s => s.Name == endpointName && s.Online) // Get the endpoint with name matching the incoming request
               ?? base.GetNegotiateEndpoint(context, endpoints); // Or fallback to the default behavior to randomly select one from primary endpoints, or fallback to secondary when no primary ones are online
    }
}

別忘了使用下列專案向 DI 容器註冊路由器:

services.AddSingleton(typeof(IEndpointRouter), typeof(CustomRouter));
services.AddSignalR()
        .AddAzureSignalR(
            options => 
            {
                options.Endpoints = new ServiceEndpoint[]
                {
                    new ServiceEndpoint(name: "east", connectionString: "<connectionString1>"),
                    new ServiceEndpoint(name: "west", connectionString: "<connectionString2>"),
                    new ServiceEndpoint("<connectionString3>")
                };
            });

針對 ASP.NET

從組態新增多個端點

使用金鑰 Azure:SignalR:ConnectionStringAzure:SignalR:ConnectionString: SignalR Service 連接字串進行設定。

如果機碼以 開頭 Azure:SignalR:ConnectionString: ,它的格式應該是 Azure:SignalR:ConnectionString:{Name}:{EndpointType} ,其中 NameEndpointType 是 物件的屬性 ServiceEndpoint ,而且可從程式碼存取。

您可以將多個實例連接字串新增至 web.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Azure:SignalR:ConnectionString" connectionString="<ConnectionString1>"/>
    <add name="Azure:SignalR:ConnectionString:en-us" connectionString="<ConnectionString2>"/>
    <add name="Azure:SignalR:ConnectionString:zh-cn:secondary" connectionString="<ConnectionString3>"/>
    <add name="Azure:SignalR:ConnectionString:Backup:secondary" connectionString="<ConnectionString4>"/>
  </connectionStrings>
  ...
</configuration>

從程式碼新增多個端點

類別 ServiceEndpoint 描述 Azure SignalR 服務端點的屬性。 使用 Azure SignalR Service SDK 時,您可以透過下列方式設定多個實例端點:

app.MapAzureSignalR(
    this.GetType().FullName, 
    options => {
            options.Endpoints = new ServiceEndpoint[]
            {
                // Note: this is just a demonstration of how to set options. Endpoints
                // Having ConnectionStrings explicitly set inside the code is not encouraged.
                // You can fetch it from a safe place such as Azure KeyVault
                new ServiceEndpoint("<ConnectionString1>"),
                new ServiceEndpoint("<ConnectionString2>"),
                new ServiceEndpoint("<ConnectionString3>"),
            }
        });

自訂路由器

ASP.NET SignalR 與 ASP.NET Core SignalR 之間的唯一差異是 的 GetNegotiateEndpoint HTTP 內容類型。 針對 ASP.NET SignalR,其為 IOwinCoNtext 類型。

下列程式碼是 ASP.NET SignalR 的自訂交涉範例:

private class CustomRouter : EndpointRouterDecorator
{
    public override ServiceEndpoint GetNegotiateEndpoint(IOwinContext context, IEnumerable<ServiceEndpoint> endpoints)
    {
        // Override the negotiate behavior to get the endpoint from query string
        var endpointName = context.Request.Query["endpoint"];
        if (string.IsNullOrEmpty(endpointName))
        {
            context.Response.StatusCode = 400;
            context.Response.Write("Invalid request.");
            return null;
        }

        return endpoints.FirstOrDefault(s => s.Name == endpointName && s.Online) // Get the endpoint with name matching the incoming request
               ?? base.GetNegotiateEndpoint(context, endpoints); // Or fallback to the default behavior to randomly select one from primary endpoints, or fallback to secondary when no primary ones are online
    }
}

別忘了使用下列專案向 DI 容器註冊路由器:

var hub = new HubConfiguration();
var router = new CustomRouter();
hub.Resolver.Register(typeof(IEndpointRouter), () => router);
app.MapAzureSignalR(GetType().FullName, hub, options => {
    options.Endpoints = new ServiceEndpoint[]
                {
                    new ServiceEndpoint(name: "east", connectionString: "<connectionString1>"),
                    new ServiceEndpoint(name: "west", connectionString: "<connectionString2>"),
                    new ServiceEndpoint("<connectionString3>")
                };
});

服務端點計量

為了啟用進階路由器,SignalR 伺服器 SDK 提供多個計量,以協助伺服器做出明智的決策。 屬性位於 底下 ServiceEndpoint.EndpointMetrics

標準名稱 描述
ClientConnectionCount 服務端點所有中樞上的並行用戶端連線總數
ServerConnectionCount 服務端點所有中樞上的並行伺服器連線總數
ConnectionCapacity 服務端點的連線配額總計,包括用戶端和伺服器連線

下列程式碼是根據 ClientConnectionCount 自訂路由器的範例。

private class CustomRouter : EndpointRouterDecorator
{
    public override ServiceEndpoint GetNegotiateEndpoint(HttpContext context, IEnumerable<ServiceEndpoint> endpoints)
    {
        return endpoints.OrderBy(x => x.EndpointMetrics.ClientConnectionCount).FirstOrDefault(x => x.Online) // Get the available endpoint with minimal clients load
               ?? base.GetNegotiateEndpoint(context, endpoints); // Or fallback to the default behavior to randomly select one from primary endpoints, or fallback to secondary when no primary ones are online
    }
}

動態調整 ServiceEndpoints

從 SDK 1.5.0 版開始,我們會先啟用 ASP.NET Core 版本的動態調整 ServiceEndpoints。 因此,當您需要新增/移除 ServiceEndpoint 時,不需要重新開機應用程式伺服器。 由於 ASP.NET Core 支援使用 reloadOnChange: true 的預設 appsettings.json 組態,因此您不需要變更程式碼,而且本質上也支援它。 如果您想要新增一些自訂群組態並使用熱重載,請參閱 ASP.NET Core 中的設定。

注意

考慮到伺服器/服務和用戶端/服務之間的連線設定時間可能不同,為了確保在調整程式期間不會遺失訊息,我們有暫存期間,等待伺服器連線準備好,再將新的 ServiceEndpoint 開啟給用戶端。 通常需要幾秒鐘的時間才能完成,而且您將能夠看到記錄訊息,例如,表示 Succeed in adding endpoint: '{endpoint}' 進程完成。

在某些情況下,例如不同應用程式伺服器上的跨區域網路問題或設定不一致,暫存期間可能無法正確完成。 在這些情況下,建議您在發現調整程式無法正常運作時重新開機應用程式伺服器。

調整的預設逾時期間為 5 分鐘,而且可以藉由變更 中的 ServiceOptions.ServiceScaleTimeout 值來自訂。 如果您有許多應用程式伺服器,建議您再擴充一點值。

跨區域案例中的設定

物件 ServiceEndpoint 具有 EndpointType 值為 primarysecondary 的屬性。

主要端點是接收用戶端流量的慣用端點,因為它們具有更可靠的網路連線。 次要端點的網路連線較不可靠,而且僅用於伺服器對用戶端流量。 例如,次要端點用於廣播訊息,而不是用戶端到伺服器流量。

在跨區域的情況下,網路可能不穩定。 對於位於 美國東部的應用程式伺服器,位於相同 美國 東部區域的 SignalR 服務端點是 primary ,而其他區域中的端點則標示為 secondary 。 在此設定中,其他區域中的服務端點可以 接收 來自美國 東部 應用程式伺服器的訊息,但不會 將跨區域 用戶端路由傳送至此應用程式伺服器。 下圖顯示架構:

Cross-Geo Infra

當用戶端嘗試 /negotiate 使用具有預設路由器的應用程式伺服器時,SDK 會隨機從一組可用的 primary 端點中選取 一個端點。 當主要端點無法使用時,SDK 會 隨機 選取所有可用的 secondary 端點。 當伺服器與服務端點之間的連線運作時,端點會標示為 可用

在跨區域案例中,當用戶端嘗試 /negotiate 使用裝載 在美國 東部的應用程式伺服器時,預設一律會 primary 傳回位於相同區域的端點。 當所有 美國 東部端點都無法使用時,路由器會將用戶端重新導向至其他區域中的端點。 下列 容錯移轉 區段將詳細說明案例。

Normal Negotiate

容錯移轉

當沒有 primary 可用的端點時,用戶端會 /negotiate 從可用的 secondary 端點中挑選。 此容錯移轉機制需要每個端點作為至少一個 primary 應用程式伺服器的端點。

Diagram showing the Failover mechanism process.

下一步

您可以在高可用性和災害復原案例中使用多個端點。