压测
tools/loadtest 是一个独立的 Go 模块,模拟很多用户同时连上一个 Talkwire 服务端:登录、两两组成单聊、按人数分群,再以固定速率发消息,测量登录耗时、pub 应答耗时、单聊与群消息的送达耗时,并统计丢失。它的依赖与 tools/conformance 相同,只有 gorilla/websocket 与 golang-jwt。
工具目前只在本机试跑过,正式压测还没做,见文末 现状。
运行
压测端需要服务端的 JWT_SECRET,用它给每个模拟用户签登录令牌。tools/loadtest/README.md 里的示例:
cd tools/loadtest
LOADTEST_JWT_SECRET=<服务端的 JWT_SECRET> go run . -url ws://127.0.0.1:6062/ws/im -users 1000 -group-size 50 -rate 1 -duration 60sLOADTEST_JWT_SECRET 少于 32 个字符时工具直接退出;-users 小于 2 或 -rate 不大于 0 时也直接退出。
参数
下表来自 tools/loadtest/main.go 的命令行定义。
| 参数 | 默认 | 作用 |
|---|---|---|
-url | ws://127.0.0.1:6062/ws/im | 服务端的 WebSocket 地址 |
-users | 100 | 用户数,每人一条连接 |
-group-size | 10 | 每个群的人数;小于 2 时不建群。用户同时两两配对发单聊 |
-rate | 1 | 每人每秒发几条,单聊与群轮流 |
-duration | 30s | 发多久 |
-ramp | 10s | 连接在这段时间里均匀建立 |
-drain | 10s | 停发之后最多再等多久收尾 |
-timeout | 15s | 连接、登录和建群各自的超时 |
-prefix | 每次运行新生成 | 用户 id 前缀 |
-max-loss | 0 | 丢失超过这个比例就以 1 退出 |
-json | 不写 | 另把结果写成 JSON 文件 |
-strict-token | true | 签 strict 档要求的令牌(type=access、role=app_user);对 legacy 档也能用 |
不给 -prefix 时每次运行生成一个新前缀(lt 加 8 位十六进制再加 -),几次运行之间互不看到对方的历史。
一次运行做什么
一次运行分四步,前一步全部完成才进入下一步:
- 连接与登录:在
-ramp内均匀地建立连接,每条连接发login。令牌有效期 2 小时。 - 配对与建群:在线用户两两配对;每
-group-size个人由第一个人发create_grp建一个群,建完等 1 秒让成员收到grp_sync完成订阅。 - 发送:每人按
-rate定速发pub,在单聊对象与群之间轮流,持续-duration。 - 收尾:停发后最多再等
-drain,直到每条消息都有应答、该送达的都已收到,然后关闭连接。
消息正文带着发送时刻(lt|<Unix 纳秒>|…),接收方据此算送达耗时。所以多台压测机的时钟要同步,或者都在同一台机器上。
测量什么
报告打印到标准输出;给了 -json 时另写一份 JSON。
| 项目 | 含义 |
|---|---|
| 在线 | 登录成功的用户数,以及连接失败、登录失败的个数和建立连接用的总秒数 |
| 群 | 建成的群数与失败数 |
| 发送 | 发出的消息数与每秒条数;写失败数;被服务端拒绝的条数(按应答码分);没等到应答的条数 |
| 送达 | 应收与实收的送达次数、每秒送达数、丢失数。一条群消息对群里每个其他成员各算一次送达 |
| closed early | 压测期间被服务端断开的连接数 |
| 耗时 | 登录(从连接到登录应答)、应答(pub 到它的应答)、单聊送达、群送达,各给出条数与 p50、p95、p99、最大值,单位毫秒 |
| 错误 | 不同的错误信息最多记 20 种,各带出现次数 |
被服务端拒绝的消息不计入应收送达。服务端发现某个连接的发送队列满了(客户端读得太慢)会断开它,这在报告里算 closed early,被断开的连接收不到的消息算丢失。
通过条件
全部用户登录成功、全部群建成、每条消息都有应答、没有连接被中途断开,并且丢失比例不超过 -max-loss 时,工具以 0 退出,否则以 1 退出。一次送达都没有的运行也算失败;参数错误以 2 退出。
服务端怎么配
压测要求所有用户互为好友、发送频率不被限流,tools/loadtest/README.md 给出的配法如下:
宿主用 standalone 模式(
TALKWIRE_HOST_MODE=standalone)的默认allow-all,见 standalone 宿主。运行配置里的发送频率默认每人 10 秒 20 条,
-rate超过 2 时要调高;建大群时把人数上限调到-group-size以上。standalone 宿主的配置文件:json{"relations": "allow-all", "runtimeConfig": {"publishRateLimitPer10s": 100000, "maxMembers": 500, "readReceiptEnabled": true}}数据库用一个可以随便写的库:每条消息都入库。
这份配置写了 runtimeConfig 却没写 fcmEnabled,离线推送因此是关着的(没写的布尔项为 false)。压测不需要推送,照抄到别的部署时要补上 "fcmEnabled": true。
现状
工具已经在本机试跑过,结果只说明工具能用、服务端在这个量级下行为正确,不代表服务端的上限。正式压测还没有做,计划在 Linux 上进行(README.md 的“当前状态”)。
本机试跑(2026-10-25)
环境是 Windows 11、i7-8700(12 个逻辑核)、16 GB;服务端、PostgreSQL 17 与压测端在同一台机器上,strict 档,standalone 宿主。压测端与服务端抢 CPU,网络是本机回环。
| 用户 | 群 | 每人每秒 | 发送 | 送达 | 丢失 | ack p50 / p99 | 群送达 p50 / p99 |
|---|---|---|---|---|---|---|---|
| 50 | 5 个 × 10 人 | 1 | 451 | 2 251 | 0 | 3.2 / 13.1 ms | 2.6 / 10.7 ms |
| 1000 | 20 个 × 50 人 | 1 | 29 003(967/s) | 725 051(24 165/s) | 0 | 2.1 / 2.9 ms | 1.6 / 2.7 ms |
| 1000 | 4 个 × 250 人 | 1 | 18 608(863/s) | 2 315 748(107 349/s) | 7 017(0.3%) | 1 034 / 6 537 ms | 1 007 / 6 464 ms |
最后一行已经饱和:每秒约 500 条群消息,每条发给 249 人。第一次跑这一档时压测端把每帧解码成通用的 map,自己先跟不上,服务端因此断开了 303 个读得慢的连接、丢了 25%;改成只解码需要的字段后,断开降到 7 个、丢失降到 0.3%。剩下的饱和是服务端的还是压测端的,在同一台机器上分不出来。
正式压测(待做)
正式压测要在 Linux 上做:服务端、数据库、压测端分开机器,压测端可以多台各跑一部分用户(各用不同的 -prefix)。同时记录服务端的 CPU、内存、goroutine 数和数据库的写入速率,逐步加大群的人数与速率,找到送达耗时开始上升、开始断开慢连接的那一档。