sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/docs/sharding.md
Currently we add support for configuring multiple SignalR Service instances. You can distribute your clients to multiple SignalR service instances and send messages to multiple instances as if to one instance.
<!-- TOC --> <!-- /TOC -->Routing logic is the way to decide to which SignalR Service instance among multiple instances your clients connect and your messages send. By applying different routing logic, this feature can be used in different scenarios.
Currently multiple-endpoint feature is only supported on Persistent transport type.
To enable multiple SignalR Service instances, you should:
Persistent transport typeThe default transport type is Transient mode. You should add the following entry to your local.settings.json file or the application setting on Azure.
{
"AzureSignalRServiceTransportType":"Persistent"
}
Notes for switching from
Transientmode toPersistentmode on Azure Functions runtime v3:Under
Transientmode, theNewtonsoft.Jsonlibrary is used to serialize arguments of hub methods. However, underPersistentmode, theSystem.Text.Jsonlibrary is used as default on Azure Functions runtime v3.System.Text.Jsonhas some key differences in default behavior withNewtonsoft.Json. If you want to useNewtonsoft.JsonunderPersistentmode, you can add the configuration item"Azure:SignalR:HubProtocol": "NewtonsoftJson"in thelocal.settings.jsonfile orAzure__SignalR__HubProtocol=NewtonsoftJsonin the Azure portal.
We use a ServiceEndpoint object to represent a SignalR Service instance. You can define a service endpoint with its <EndpointName> and <EndpointType> in the entry key, and the connection string in the entry value. The keys are in the following format:
Azure:SignalR:Endpoints:<EndpointName>:<EndpointType>
<EndpointType> is optional and defaults to primary. See samples below:
{
"Azure:SignalR:Endpoints:EndpointName1":"<ConnectionString>",
"Azure:SignalR:Endpoints:EndpointName2:Secondary":"<ConnectionString>",
"Azure:SignalR:Endpoints:EndpointName3:Primary":"<ConnectionString>"
}
When you configure Azure SignalR endpoints in the App Service in the Azure portal, replace
":"with"__"(the double underscore) in the keys. For reasons, see Environment variables.A connection string configured with the key
{ConnectionStringSetting}(defaults to "AzureSignalRConnectionString") is also recognized as a primary service endpoint with empty name. But this configuration style isn't recommended for multiple endpoints.
The SignalR Service Owner role is required to use an Identity-based connection.
Here's an example to configure Azure Identity for a SignalR endpoint named "Endpoint1":
{
"Values": {
"Azure:SignalR:Endpoints:Endpoint1:serviceUri": "https://<SignalRServiceHost>",
"Azure:SignalR:Endpoints:Endpoint1:clientId": "...",
"Azure:SignalR:Endpoints:Endpoint1:clientSecret": "...",
"Azure:SignalR:Endpoints:Endpoint1:tenantId": "..."
}
}
The serviceUri is required. Other items, such as clientId and clientSecret, are optional depending upon which credentials you want to use.
For more information, see Common properties for Identity-based connections. Note that you should replace <CONNECTION_NAME_PREFIX> there with Azure__SignalR__Endpoints__<EndpointName>.
By default, the SDK uses the DefaultEndpointRouter to pick up endpoints.
Client routing: Randomly select one endpoint from primary online endpoints. If all the primary endpoints are offline, then randomly select one secondary online endpoint. If the selection fails again, then exception is thrown.
Server message routing: All service endpoints are returned.
Here are the steps:
Implement a customized router. You can leverage information provided from ServiceEndpoint to make routing decision. See guide here: customize-route-algorithm. Please note that Http trigger is required in the negotiation function when you need HttpContext in custom negotiation method.
Register the router to DI container.
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>();
}
}
}
For non-.NET languages, we support specifying target endpoints in each request. You'll use new binding types to get endpoint information.
The SignalRConnectionInfo binding selects one endpoint according the default routing rule. If you want to customize routing rule, you should use SignalRNegotiation binding instead of SignalRConnectionInfo binding.
SignalRNegotiation binding configuration properties are the same as SignalRConnectionInfo. Here's a function.json file sample:
{
"type": "signalRNegotiation",
"name": "negotiationContext",
"hubName": "<HubName>",
"direction": "in"
}
You could also add other binding data such as userId, idToken and claimTypeList just like SignalRConnectionInfo.
The object you get from SignalRNegotiation binding is in the following format:
{
"endpoints": [
{
"endpointType": "Primary",
"name": "<EndpointName>",
"endpoint": "https://****.service.signalr.net",
"online": true,
"connectionInfo": {
"url": "<client-access-url>",
"accessToken": "<client-access-token>"
}
},
{
"...": "..."
}
]
}
Here's a Javascript usage sample of SignalRNegotiation binding:
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;
}
}
Messages or actions routing needs two binding types to cooperate. In general, firstly you need a new input binding type SignalREndpoints to get all the available endpoint information. Then you filter the endpoints and get an array containing all the endpoints that you want to send to. Lastly you specify the target endpoints in the SignalR output binding.
Here's the SignalREndpoints binding configuration properties in functions.json file:
{
"type": "signalREndpoints",
"direction": "in",
"name": "endpoints",
"hubName": "<HubName>"
}
The object you get from SignalREndpoints is an array of endpoints each of which is represented as a JSON object with the following schema:
{
"endpointType": "<EndpointType>",
"name": "<EndpointName>",
"endpoint": "https://****.service.signalr.net",
"online": true
}
After you get the target endpoint array, add an endpoints property to the output binding object. This is a Javascript example:
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();
}