直播系统聊天服务基于 Workerman,若同时在线 1 万人至 10 万人,技术上需要做哪些调整?

creatorwpy

直播系统架构咨询

背景

我开发的直播系统,聊天服务基于 Workerman GatewayWorker 框架开发。主要功能是接收并转发聊天消息,用户送礼物时调用 API 扣款,然后在房间内广播。
当前部署为单台独立聊天服务器,功能相对精简,H5 预览地址:https://smplive.wpygo.com/preview/

问题描述

我想请教的是,如果同时在线 1 万人(分布在 10 个房间,每房间 1 千人),当单个用户发送一条消息时,需广播给房间内 1 千人,这对带宽压力很大。

请教内容

针对这种情况,服务器代码及架构需要做哪些调整?若扩展到 10 万人 在线,又该如何规划?

378 5 6
5个回答

creatorwpy

该软件的功能及技术介绍在 https://smplive.wpygo.com/

  • 暂无评论
北月妖王

在 Workerman 社区,大家习惯了卷高性能长连接,但有时候“跳出三界外”,利用互联网的基础设施(CDN)来解决业务瓶颈,可以换个思路来玩一玩 骚操作


1. Workerman 侧:内存写,定时落盘

不要在每个业务逻辑里都去写 json 文件,那样会把磁盘 IO 跑爆。

  • 实现逻辑:BusinessWorker 收到消息/点赞后,只往 Redis 或者 内存数组 里丢。
  • 定时任务:在 onWorkerStart 里开一个 Timer,每 500ms 或 1s 执行一次:
    1. 从内存提取最新的 50 条消息。
    2. 生成 JSON 字符串。
    3. 原子性写入:先写到 chat.json.tmp,然后 renamechat.json
    4. 目录挂载在 /dev/shm(内存磁盘)。
use Workerman\Timer;

// 假设这是在某个 Worker 进程中
$worker->onWorkerStart = function() {
    // 每秒执行一次,生成静态快照
    Timer::add(1, function() {
        $latestMessages = get_messages_from_redis(50); 
        $jsonContent = json_encode([
            'time' => time(),
            'list' => $latestMessages,
            'count' => get_total_likes_from_redis()
        ]);
        // 写入内存文件系统,快如闪电
        file_put_contents('/dev/shm/live_room_1.json', $jsonContent);
    });
};

2. CDN 侧:要把“缓存”当成你的“数据库”

这是最关键的一环。你的域名(比如 static.live.com)必须配置:

  • Cache-Control: public, max-age=1, s-maxage=1
    • 这告诉 CDN:这个文件每秒钟去源站校验一次。
    • 即使有 100 万人请求,CDN 边缘节点每秒也只会产生 1 个 回源请求。
  • 忽略参数缓存:在 CDN 后台开启“忽略 URL 参数”,这样无论前端怎么加随机数,CDN 都认为是一个文件,从而保证缓存命中率。

3. 前端侧:不仅是下载,还要“缝合”

前端如果只是简单的 innerHTML = json,屏幕会闪烁,体验极差。

  • 增量渲染:前端本地维护一个 messageList,每次 fetch 到 JSON 后,对比 msg_id,只把本地没有的记录追加到屏幕上。
  • 平滑动画:点赞数如果从 100 变成 150,不要直接改数字,用个简单的 JS 计数器跑个 1 秒钟的数字滚动动画,用户根本感觉不到这是“轮询”来的。

4. 为什么这招能省大钱?

  1. 连接数归零:服务器不再需要维持 10 万个维持心跳的 TCP 连接,CPU 占用率从 80% 降到 5%。
  2. 带宽转移:原本昂贵的 BGP 全站带宽,变成了廉价的 CDN 流量计费。
  3. 无状态扩容:如果你要支持 100 万人,你不需要加 Workerman 服务器,你只需要给 CDN 余额充钱。

5. 注意点!

对普通白嫖用户来说,这一点延迟其实不算什么,但是花了钱的用户天然值得更好的实时体验,所以得给大哥大姐们提供一个通道优化。

用户类型 通道策略 体验
普通观众 CDN 轮询为主 延迟 ≤1s,够用
活跃用户(高频发言) 混合:轮询 + WS 接收插队消息 看到大哥消息实时,自己发的消息本地乐观更新
付费用户 / 大哥 全量 WebSocket 完全实时,体验无感知延迟
主播 全量 WebSocket 必须,还要加管理权限通道

WebSocket 推下来的礼物/付费弹幕,直接 unshift 到列表顶部或者触发动画层,不走轮询的合并逻辑,两套渲染管线互不干扰。

  • creatorwpy 2026-03-25

    谢谢,我得好好消化一下

  • creatorwpy 2026-03-26

    非常感谢。
    我网上搜索了下,cdn是可以支持WebSocket协议(WSS加速功能),这个我之前不知道。
    我终结一下是:
    1.服务器收到消息就向Redis里写数据。
    2.定时任务每1秒取最新的前50条写入文件
    3.用户连接cdn,设置缓存1秒。这样服务器每秒只能收到1条的cdn请求,到得上面的50条数据
    4.用户通过cdn拿到50条数据,显示给用户

    我在其它方案里也看到过只取部分消息广播。如果人特别多,消费会显示到用户看不到。广播部分消息也合理。

kof21411

不是说Workerman不好,若真扩展到 10 万人 在线的,建议换go吧

胡桃

抖音十万人的直播间也是屈指可数,敢问阁下高就?

  • creatorwpy 2026-03-26

    谢谢您的评论。您说的对,不是真的十万人在线,我就是想请教一下,如果有十万人在线,有那些技术方案。

xiuwang

你看各大平台不管直播间是10万人还是更多人,都是以固定频率大概每秒推送2-3条。
所以带宽占用基本和人数基本是线性增加的。
假设每次10万人在线,每秒推送的数据200字节,大概需要带宽152Mb/S。
腾讯云有200M带宽的轻量服务器,一个月几十块,一台处理2万连接很轻松,Gateway支持分布式部署,弄个几台作为Gateway服务就够用了,用不到什么规划。
负载均衡直接用DNS配置多个A记录即可。

  • creatorwpy 2026-03-26

    谢谢。这也是一个很好的方案,并且基本不需要修改程序。我看到的所有方案中,都不会所有消息全部广播。这个我记住了。DNS配置多个A记录这个是好方案。

🔝