You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多SignalR服务器场景下如何实现连接负载均衡?

SignalR 集群Redis背板下的连接负载均衡实现方案

关于负载均衡器处理SignalR连接的疑问

你的假设不完全准确:

  • 现代主流负载均衡器(如Nginx、HAProxy、云厂商LB)基于异步非阻塞架构,能够承载大量WebSocket长连接,只要配置合理(调整连接数上限、超时时间等),单台LB足以应对大部分场景的SignalR连接需求。
  • 如果连接量极大超出单台LB的处理能力,可以通过LB集群(比如DNS轮询+多台LB)横向扩展,而非必须让客户端直接绕过LB连接SignalR服务器。
  • 你设想的“客户端直接连指定SignalR服务器”属于客户端感知路由方案,确实能减轻LB的长连接压力,但需要额外处理客户端重连时的故障转移逻辑(比如目标服务器下线时,客户端要重新向LB请求新的服务器地址)。

你的方案具体实现步骤

不需要禁用SignalR默认的WebSocket端点,只需通过授权机制保护端点,配合LB的令牌分发逻辑即可实现你的流程。

1. SignalR 服务器端配置:令牌授权保护Hub端点

在ASP.NET Core SignalR中,通过JWT令牌验证实现授权,确保只有LB签发的有效令牌才能建立连接:

配置JWT验证服务

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        // 配置与LB一致的密钥、签发方、受众信息
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "your-lb-identifier", // LB的唯一标识
            ValidateAudience = true,
            ValidAudience = "your-signalr-cluster", // SignalR集群标识
            ValidateLifetime = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-shared-secret-key"))
        };

        // 处理SignalR WebSocket连接的令牌提取逻辑
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                // 从查询参数或请求头提取令牌(根据客户端传递方式调整)
                var accessToken = context.Request.Query["access_token"] ?? 
                                 context.Request.Headers.Authorization.ToString().Replace("Bearer ", "");
                
                if (!string.IsNullOrEmpty(accessToken) &&
                    context.HttpContext.Request.Path.StartsWithSegments("/hubs/yourhub"))
                {
                    context.Token = accessToken;
                }
                return Task.CompletedTask;
            }
        };
    });

// 添加自定义授权策略,验证令牌中的服务器标识
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("SignalRLBPolicy", policy =>
    {
        policy.RequireAuthenticatedUser();
        // 验证令牌是否允许连接当前服务器(防止令牌跨服务器复用)
        policy.RequireClaim("AllowedServer", Environment.MachineName); // 用服务器唯一标识替代MachineName
    });
});

配置SignalR Hub并应用授权策略

app.UseAuthentication();
app.UseAuthorization();

// 给Hub端点绑定授权策略,未携带有效令牌的请求会被拒绝
app.MapHub<YourChatHub>("/hubs/yourhub")
    .RequireAuthorization("SignalRLBPolicy");

2. 负载均衡器端:实现令牌分发逻辑

LB需要提供一个HTTP接口(比如/signalr/assign-server),执行以下逻辑:

  • 根据负载算法(轮询、最少连接数、加权轮询等)选择一台可用的SignalR服务器。
  • 生成短生命周期(比如5分钟)的JWT令牌,令牌中需包含以下声明:
    • exp:过期时间,确保令牌无法长期复用
    • AllowedServer:选中的SignalR服务器的唯一标识(与SignalR服务器配置的验证字段一致)
    • sub:用户唯一标识(可选,用于后续SignalR的用户身份识别)
  • 将令牌和选中的SignalR服务器的WebSocket端点URI(比如wss://signalr-server-1.example.com/hubs/yourhub)返回给客户端。

3. 客户端连接逻辑

客户端需要先调用LB的分发接口获取令牌和服务器地址,再发起SignalR连接:

// 先请求LB获取令牌和服务器地址
async function getSignalRAssignment() {
    const response = await fetch('https://your-lb.example.com/signalr/assign-server', {
        method: 'GET',
        credentials: 'include' // 如果需要用户身份验证的话
    });
    return await response.json(); // 返回格式:{ accessToken: "...", serverUrl: "wss://..." }
}

// 建立SignalR连接
async function connectSignalR() {
    const assignment = await getSignalRAssignment();
    const connection = new signalR.HubConnectionBuilder()
        .withUrl(assignment.serverUrl, {
            accessTokenFactory: () => assignment.accessToken
        })
        .build();

    // 处理连接失败,重新请求LB获取新的服务器地址
    connection.onclose(async () => {
        await connectSignalR();
    });

    await connection.start();
    console.log('SignalR连接成功');
}

// 启动连接
connectSignalR();

关键注意事项

  • 令牌的密钥要在LB和所有SignalR服务器之间保密,避免泄露。
  • 短生命周期令牌可以降低令牌被盗用的风险,同时配合客户端连接失败重连逻辑,确保服务器下线时能自动切换。
  • SignalR服务器要定期向LB上报自身健康状态,LB只分发请求给健康的服务器。

内容的提问来源于stack exchange,提问作者Szyszka947

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 17:16:06