负载均衡与健康检查:Nginx 配置 WebSocket 负载均衡、健康检查端点

信令服务器跑起来了,单个节点也能正常工作。但问题来了——用户量一上来,单节点扛得住吗?我在早期的一个项目中就吃过这个亏。当时觉得信令服务器轻量,一台机器绰绰有余。结果上线第三天,并发连接数直接打穿CPU,用户集体掉线。嗯,从那以后,我再也不敢轻视负载均衡了。

说白了,WebRTC的信令服务器虽然不像媒体服务器那样吃带宽,但它要维持大量长连接。单节点一旦挂掉,所有用户的通话都会中断。所以我们需要Nginx来做两件事:把流量分散到多个信令节点,以及自动剔除故障节点

为什么WebSocket负载均衡比HTTP更棘手?

你想想看,普通HTTP请求是短连接,请求完就断开。Nginx随便轮询一下就行。但WebSocket是长连接,一旦建立,客户端和服务器要保持长时间的通信。

这里有个坑:WebSocket的升级过程。客户端先发一个HTTP Upgrade请求,Nginx得把这个请求转发到同一个后端节点。如果后续的WebSocket帧被转发到别的节点,连接就断了。

我在项目中遇到过这种情况:配置了普通的HTTP负载均衡,结果WebSocket连接频繁断开。查了半天才发现,Nginx默认的负载均衡策略会把WebSocket的后续帧分发到不同节点。解决方案就是启用ip_hashsticky会话保持。

Nginx配置WebSocket负载均衡

先看一个基础配置。我个人习惯把WebSocket的负载均衡单独写一个upstream块,方便管理。

upstream signaling_servers {
    # 使用ip_hash保证同一个客户端的请求始终落到同一台后端
    ip_hash;

    # 后端信令服务器列表
    server 192.168.1.10:8080 weight=5 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 weight=5 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 backup;  # 备用节点
}

server {
    listen 443 ssl;
    server_name signaling.example.com;

    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;

    location /ws {
        # 关键:升级WebSocket协议
        proxy_pass http://signaling_servers;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # 保持长连接的超时设置
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;

        # 传递真实IP
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
    }
}

这里有几个要点:

  • ip_hash:确保同一个IP的请求始终转发到同一台后端。WebSocket长连接必须这么做。
  • proxy_read_timeout:设长一点。WebSocket连接可能持续几十分钟甚至几小时,默认60秒超时会导致连接被切断。
  • Upgrade头:必须显式传递,否则Nginx不会识别WebSocket协议。
注意:ip_hash在客户端使用NAT或代理时会失效。所有来自同一个公网IP的请求都会落到同一台后端。如果用户量很大,可以考虑用sticky模块或基于Cookie的会话保持。

健康检查:自动剔除故障节点

光有负载均衡还不够。如果某个信令服务器挂了,Nginx还继续往它转发请求,那用户就彻底连不上了。我们需要健康检查来自动剔除故障节点。

Nginx开源版自带的健康检查比较基础,就是上面配置里的max_failsfail_timeout。它的逻辑是:如果连续max_fails次请求失败,就把这个节点标记为不可用,等待fail_timeout秒后再尝试。

但这种方式有个问题:它只能检测到Nginx转发请求失败的情况。如果后端进程还在,但业务逻辑已经卡死(比如内存泄漏导致响应缓慢),Nginx可能检测不到。

所以我建议在信令服务器里单独暴露一个健康检查端点。这个端点不参与业务逻辑,只做简单的存活检测。

实现健康检查端点

以Node.js为例,在信令服务器里加一个轻量级的HTTP端点:

const http = require('http');
const express = require('express');
const app = express();

// 健康检查端点
app.get('/health', (req, res) => {
    // 检查WebSocket服务器是否正常运行
    const wsServer = req.app.get('wsServer');
    const isHealthy = wsServer && wsServer._server.listening;

    if (isHealthy) {
        res.status(200).json({
            status: 'ok',
            uptime: process.uptime(),
            connections: wsServer.clients.size
        });
    } else {
        res.status(503).json({
            status: 'error',
            message: 'WebSocket server not running'
        });
    }
});

// 信令服务器主逻辑
const server = http.createServer(app);
const WebSocket = require('ws');
const wss = new WebSocket.Server({ server });
app.set('wsServer', wss);

server.listen(8080, () => {
    console.log('Signaling server running on port 8080');
});

这个端点返回的信息很关键:

  • HTTP状态码:200表示正常,503表示异常。Nginx的负载均衡器会根据状态码判断节点是否健康。
  • 连接数:可以辅助判断节点是否过载。如果连接数超过阈值,可以考虑暂时不分配新连接。
  • 运行时间:快速判断节点是否刚重启过。

Nginx主动健康检查(商业版 vs 开源方案)

Nginx Plus(商业版)内置了主动健康检查功能,可以定期向后端发送请求,检测端点是否正常。配置如下:

upstream signaling_servers {
    zone signaling 64k;

    server 192.168.1.10:8080;
    server 192.168.1.11:8080;

    # 主动健康检查
    health_check interval=5s fails=3 passes=2 uri=/health;
}

但如果你用的是Nginx开源版,也有替代方案。我推荐用nginx_upstream_check_module这个第三方模块。它提供了类似商业版的主动健康检查功能。

安装这个模块后,配置如下:

upstream signaling_servers {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;

    # 主动健康检查
    check interval=3000 rise=2 fall=5 timeout=1000 type=http;
    check_http_send "GET /health HTTP/1.0\r\n\r\n";
    check_http_expect_alive http_2xx http_3xx;
}

参数说明:

参数 含义 推荐值
interval 检查间隔(毫秒) 3000(3秒)
rise 连续成功几次后标记为健康 2
fall 连续失败几次后标记为不健康 5
timeout 检查请求超时时间(毫秒) 1000
我的经验:检查间隔不要设太短。我曾经设了1秒一次,结果后端服务器压力一大,健康检查请求反而成了压垮骆驼的最后一根稻草。3-5秒是比较合理的间隔。

整体架构图

下面这张图展示了Nginx负载均衡+健康检查的完整流程:

WebRTC客户端 WebSocket连接 Nginx 负载均衡 + 健康检查 信令节点1 192.168.1.10:8080 信令节点2 192.168.1.11:8080 备用节点 192.168.1.12:8080 /health 端点 健康检查 图例: WebSocket流量 健康检查请求 备用节点(故障时启用)

配置验证与测试

配置写好了,怎么验证它是否生效?我一般会做这几步:

  1. 检查Nginx配置语法nginx -t,确保没有拼写错误。
  2. 模拟节点故障:手动停掉一个信令服务器进程,观察Nginx是否把请求转发到其他节点。
  3. 查看健康检查日志:在Nginx的access log里,可以看到健康检查请求的痕迹。如果某个节点连续返回503,Nginx会把它标记为不可用。
  4. 压力测试:用WebSocket压测工具(比如wsbench)模拟大量连接,观察各节点的负载是否均衡。
核心要点:
  • WebSocket负载均衡必须启用会话保持(ip_hash或sticky)
  • 健康检查端点要独立于业务逻辑,只做存活检测
  • 主动健康检查比被动检测更可靠,能提前发现故障
  • 备用节点可以在主节点全部故障时兜底

我曾经在一个生产环境中遇到过这样的情况:三个信令节点,其中一个因为内存泄漏导致响应越来越慢,但进程还在。被动健康检查检测不到,Nginx继续往它转发请求,结果那台机器上的用户全部卡顿。后来加了主动健康检查,检测到响应时间超过阈值就自动剔除,问题才解决。

嗯,负载均衡和健康检查这块,说白了就是给信令服务器加一层保险。配置起来不复杂,但能避免很多线上事故。你想想看,用户正在视频通话,突然掉线了,那体验得多糟糕。所以这一步,值得花时间做好。


公众号:蓝海资料掘金营,微信deep3321