使用多個執行個體來調整 SignalR Service

SignalR Service SDK 支援 SignalR Service 執行個體的多個端點。 您可以使用這項功能來調整並行連線,或用來進行跨區域傳訊。

對於 ASP.NET Core

從組態新增多個端點

使用 SignalR Service 連接字串的機碼 Azure:SignalR:ConnectionStringAzure:SignalR:ConnectionString: 進行設定。

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

您可以使用下列 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>

從程式碼新增多個端點

ServiceEndpoint 類別描述 Azure SignalR Service 端點的屬性。 使用 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

從組態新增多個端點

使用 SignalR Service 連接字串的機碼 Azure:SignalR:ConnectionStringAzure:SignalR:ConnectionString: 進行設定。

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

您可以將多個執行個體連接字串新增至 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 Service 端點的屬性。 使用 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
    }
}

動態調整 ServiceEndpoint

從 SDK 1.5.0 版開始,我們會先為 ASP.NET Core 版本啟用動態調整 ServiceEndpoint。 因此,當您需要新增/移除 ServiceEndpoint 時,不需要重新啟動應用程式伺服器。 由於 ASP.NET Core 支援預設設定 (例如具有 reloadOnChange: trueappsettings.json),因此程式碼無須變更,原本就受支援。 而且,如果您想要新增一些自訂設定並使用熱重載,請參閱 ASP.NET Core 中的設定

注意

考量到伺服器/服務與用戶端/服務之間的連線設定時間可能不同,為了確保在調整期間不會遺失任何訊息,我們會保留預備期間以等候伺服器連線就緒,然後再向用戶端開放新的 ServiceEndpoint。 此程序通常只需幾秒鐘即可完成,且您將看到如下的 Succeed in adding endpoint: '{endpoint}' 記錄訊息,指出程序已完成。

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

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

注意

目前只有 Persistent 傳輸類型支援多重端點功能。

針對 SignalR 函式延伸模組

組態

若要啟用多個 SignalR Service 執行個體,您應:

  1. 使用 Persistent 傳輸類型。

    預設傳輸類型為 Transient 模式。 您應將下列項目新增至 local.settings.json 檔案或 Azure 上的應用程式設定。

    {
        "AzureSignalRServiceTransportType":"Persistent"
    }
    

    注意

    Transient 模式切換為 Persistent 模式時,可能會出現 JSON 序列化行為變更,因為在 Transient 模式下,會使用 Newtonsoft.Json 程式庫來序列化中樞方法的引數,但在 Persistent 模式下依預設會使用 System.Text.Json 程式庫。 System.Text.JsonNewtonsoft.Json 的預設行為有一些重要差異。 如果您想要在 Persistent 模式下使用 Newtonsoft.Json,您可以在 local.settings.json 檔案或 Azure__SignalR__HubProtocol=NewtonsoftJson Azure 入口網站上新增設定項目 "Azure:SignalR:HubProtocol":"NewtonsoftJson"

  2. 在您的設定中設定多個 SignalR Service 端點項目。

    我們使用 ServiceEndpoint 物件來表示 SignalR Service 執行個體。 您可以使用項目索引鍵中的 <EndpointName><EndpointType> 以及項目值中的連接字串來定義服務端點。 索引鍵的格式如下:

    Azure:SignalR:Endpoints:<EndpointName>:<EndpointType>
    

    <EndpointType> 是選擇性的,預設為 primary。 請參閱下方的範例:

    {
        "Azure:SignalR:Endpoints:EastUs":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:EastUs2:Secondary":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:WestUs:Primary":"<ConnectionString>"
    }
    

    注意

    • 當您在 Azure 入口網站上的 App Service 中設定 Azure SignalR 端點時,別忘了將 ":" 取代為 "__" (索引鍵中的雙底線)。 若要了解原因,請參閱環境變數

    • 使用索引鍵 {ConnectionStringSetting} (預設為 "AzureSignalRConnectionString") 設定的連接字串,也會識別為具有空名稱的主要服務端點。 不過,對於不建議將此設定樣式用於多個端點。

路由

預設行為

根據預設,函式繫結會使用 DefaultEndpointRouter 來挑選端點。

  • 用戶端路由:從主要線上端點隨機選取一個端點。 如果所有主要端點均離線,則隨機選取一個次要線上端點。 如果選取再次失敗,則會擲回例外狀況。

  • 伺服器訊息路由:傳回所有服務端點。

自訂

C# 內含式模型

以下為其步驟:

  1. 實作自訂路由器。 您可以利用從 ServiceEndpoint 提供的資訊進行路由決策。 請參閱這裡的指南:customize-route-algorithm請注意,當您的自訂交涉方法需要 HttpContext 時,交涉函式中必須要有 HTTP 觸發程序。

  2. 將路由器註冊至 DI 容器。

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.SignalR;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(SimpleChatV3.Startup))]
namespace SimpleChatV3
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddSingleton<IEndpointRouter, CustomizedRouter>();
        }
    }
}
隔離程序模型

對於在隔離程序模型上執行的函式,我們支援在每個要求中指定目標端點。 您將使用新的繫結類型來取得端點資訊。

用戶端路由

SignalRConnectionInfo 繫結會根據預設路由規則選取一個端點。 如果您想要自訂路由規則,應使用 SignalRNegotiation 繫結,而不是 SignalRConnectionInfo 繫結。

SignalRNegotiation 繫結設定屬性與 SignalRConnectionInfo 相同。 以下是 function.json 檔案範例:

{
    "type": "signalRNegotiation",
    "name": "negotiationContext",
    "hubName": "<HubName>",
    "direction": "in"
}

您也可以新增其他繫結資料,例如 userIdidTokenclaimTypeList,就像 SignalRConnectionInfo 一樣。

您從 SignalRNegotiation 繫結取得的物件採用下列格式:

{
    "endpoints": [
        {
            "endpointType": "Primary",
            "name": "<EndpointName>",
            "endpoint": "https://****.service.signalr.net",
            "online": true,
            "connectionInfo": {
                "url": "<client-access-url>",
                "accessToken": "<client-access-token>"
            }
        },
        {
            "...": "..."
        }
    ]
}

以下是 SignalRNegotiation 繫結的 JavaScript 使用範例:

module.exports = function (context, req, negotiationContext) {
    var userId = req.query.userId;
    if (userId.startsWith("east-")) {
        //return the first endpoint whose name starts with "east-" and status is online.
        context.res.body = negotiationContext.endpoints.find(endpoint => endpoint.name.startsWith("east-") && endpoint.online).connectionInfo;
    }
    else {
        //return the first online endpoint
        context.res.body = negotiationContext.endpoints.filter(endpoint => endpoint.online)[0].connectionInfo;
    }
}
訊息路由

訊息或動作路由需仰賴兩種繫結類型搭配運作。 一般而言,首先必須要有新的輸入繫結類型 SignalREndpoints,以取得所有可用的端點資訊。 接著您將篩選端點,並取得要傳送到的所有端點所屬的陣列。 最後,您會在 SignalR 輸出繫結中指定目標端點。

以下是 functions.json 檔案中的 SignalREndpoints 繫結設定屬性:

{
      "type": "signalREndpoints",
      "direction": "in",
      "name": "endpoints",
      "hubName": "<HubName>"
}

您從 SignalREndpoints 取得的物件是一個端點陣列,其中,每個端點均表示為具有下列結構描述的 JSON 物件:

{
    "endpointType": "<EndpointType>",
    "name": "<EndpointName>",
    "endpoint": "https://****.service.signalr.net",
    "online": true
}

在取得目標端點陣列後,將 endpoints 屬性新增至輸出繫結物件。 這是 JavaScript 範例:

module.exports = function (context, req, endpoints) {
    var targetEndpoints = endpoints.filter(endpoint => endpoint.name.startsWith("east-"));
    context.bindings.signalRMessages = [{
        "target": "chat",
        "arguments": ["hello-world"],
        "endpoints": targetEndpoints,
    }];
    context.done();
}

針對管理 SDK

從組態新增多個端點

使用 SignalR Service 連接字串的索引鍵 Azure:SignalR:Endpoints 進行設定。 如果索引鍵的格式應為 Azure:SignalR:Endpoints:{Name}:{EndpointType},其中,NameEndpointTypeServiceEndpoint 物件的屬性,且可從程式碼存取。

您可以使用下列 dotnet 命令新增多個執行個體連接字串:

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

從程式碼新增多個端點

ServiceEndpoint 類別描述 Azure SignalR Service 端點的屬性。 使用 Azure SignalR 管理 SDK 時,可以透過下列方式設定多個執行個體端點:

var serviceManager = new ServiceManagerBuilder()
                    .WithOptions(option =>
                    {
                        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"),
                        };
                    })
                    .BuildServiceManager();

自訂端點路由器

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

預設行為

  • 用戶端要求路由:

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

  • 伺服器訊息路由:

    將訊息傳送至特定連線,且目標連線路由至目前的伺服器時,訊息會直接送往該連線的端點。 否則,訊息會廣播至每個 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 容器註冊路由器:

var serviceManager = new ServiceManagerBuilder()
                    .WithOptions(option =>
                    {
                        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"),
                        };
                    })
                    .WithRouter(new CustomRouter())
                    .BuildServiceManager();

跨區域案例中的設定

ServiceEndpoint 物件具有值為 primarysecondaryEndpointType 屬性。

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

在跨區域案例中,網路可能不穩定。 若有一個應用程式伺服器位於美國東部,則位於相同美國東部區域的 SignalR Service 端點為 primary,而其他區域中的端點可標示為 secondary。 在此設定中,其他區域中的服務端點可接收來自此美國東部應用程式伺服器的訊息,但不會有跨區域用戶端路由至此應用程式伺服器。 下圖說明此架構:

跨地理位置的基礎結構

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

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

正常交涉

容錯移轉

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

顯示容錯移轉機制程序的圖表。

下一步

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