监控与告警:在线数、消息延迟与连接质量

长连接服务的可用性不能只看「接口返回 200」,而要看「在线数有没有掉、消息有没有积压、连接质量有没有劣化」。四类指标缺一不可:连接数量类、连接质量类、消息延迟类、进程资源类。少了延迟类,故障只能靠用户投诉发现;少了质量类,就只能看到「掉线了」而不知道为什么掉。

指标清单

指标名类型含义用途
im_ws_online_connections{app,gateway}Gauge当前在线连接数最核心的业务指标,告警首选
im_ws_online_users{app}Gauge在线用户数(多端去重)与连接数之比反映多端与重连情况
im_ws_connects_total{app,gateway,result}Counter建连次数,result=ok|fail建连速率与失败率
im_ws_disconnects_total{app,gateway,reason}Counter断开次数,按原因分类定位掉线根因
im_ws_handshake_duration_seconds{stage}Histogram握手耗时,stage=upgrade|auth|register区分是 TLS、鉴权还是注册慢
im_ws_messages_received_total{type}Counter上行消息数(区分业务与心跳)业务量与心跳分开看
im_ws_messages_sent_total{type,result}Counter下行消息数与发送结果发送失败率
im_ws_push_duration_seconds{target}Histogram推送耗时,target=unicast|multicast|broadcast推送延迟分位
im_ws_write_queue_length{gateway}Histogram写队列长度分布背压前兆
im_ws_dropped_total{reason}Counter丢弃数,reason=queue_full|slow_client|rate_limited主动丢弃必须可观测
im_ws_heartbeat_rtt_secondsHistogram心跳往返延迟连接质量的核心指标
im_ws_auth_errors_total{reason}Counter鉴权失败原因分布密钥轮换或恶意连接
go_goroutines / process_open_fds / process_max_fdsGauge协程、fd 与上限泄漏与容量水位

标签纪律:绝对不要用 uid、conn_id、room_id、msg_id 做 label。它们的基数等于在线数或消息数,会直接把时间序列数据库打爆;这类维度只能进日志或链路追踪。

常用查询

# 在线数(分实例)
im_ws_online_connections
# 5 分钟净增,区分「正常波动」与「持续泄漏/掉线」
sum(im_ws_online_connections) - sum(im_ws_online_connections offset 5m)
# 建连成功速率与失败率
sum(rate(im_ws_connects_total{result="ok"}[1m]))
sum(rate(im_ws_connects_total{result="fail"}[1m]))
  / sum(rate(im_ws_connects_total[1m]))
# 断开原因分布(Top5),一眼看出是超时还是客户端主动关
topk(5, sum(rate(im_ws_disconnects_total[5m])) by (reason))
# 推送延迟 P99(按单播/组播/广播分开)
histogram_quantile(0.99, sum(rate(im_ws_push_duration_seconds_bucket[5m])) by (le, target))
# 每用户连接数,异常升高说明客户端在疯狂重连
sum(im_ws_online_connections) / sum(im_ws_online_users)
# 协程/连接比,正常应接近 2~3
sum(go_goroutines) / sum(im_ws_online_connections)
# 丢弃率
sum(rate(im_ws_dropped_total[5m])) / sum(rate(im_ws_messages_sent_total[5m]))
curl -s 'http://127.0.0.1:9090/api/v1/query' \
  --data-urlencode 'query=sum(im_ws_online_connections)' | jq -r '.data.result[0].value[1]'
102437

排查时必须能按实例下钻:整体在线数只掉 3%,但某一台网关掉了 90%,全局平均值会完全掩盖这个事实。

消息延迟怎么量

延迟要拆成三段测,混在一起就永远说不清是客户端慢还是服务端慢。

段落测量方式目标值(同城)判读
上行(客户端到服务端)消息体带 client_ts,收到的时刻相减P99 < 150ms受客户端时钟影响,只看相对变化
服务端内部消息入队时刻到写成功时刻P99 < 50ms最干净的指标,也是告警首选
下行(服务端到客户端)客户端回 ACK 带 msg_id,发送到 ACK 的耗时P99 < 200ms与心跳 RTT 交叉验证
心跳 RTTping 发出到收到 pongP99 < 100ms反映连接质量基线,不掺业务逻辑

内部延迟用一行代码就能埋好:

start := time.Now()
if err := c.Send(msg); err == nil {
    pushDuration.WithLabelValues("unicast").Observe(time.Since(start).Seconds())
}

告警阈值建议

告警触发条件级别处置
在线数骤降5 分钟跌幅超过基线 20%(且绝对值 > 5000)P0先看是否全实例同跌,再查网关与代理
建连失败率失败率 > 1% 持续 3 分钟P1看 handshake_duration 是哪一段变慢
推送延迟劣化push P99 > 500ms 持续 5 分钟P1查写队列水位、慢网关、GC
写队列水位队列长度 P99 超过容量 80% 持续 2 分钟P1触发背压处置,摘除慢客户端
消息丢弃丢弃率 > 0.1% 持续 5 分钟P2评估限流阈值与客户端版本
协程泄漏前兆协程/连接比 > 5,或 1 小时涨幅 > 20%P1按第 17 章抓栈快照
fd 水位process_open_fds / process_max_fds > 0.8P1提升上限并查泄漏
读超时占比read_timeout 占断开原因 > 20%P2排查网络、客户端心跳、代理 idle 超时
鉴权失败突增5 分钟内失败数超过基线 10 倍P1区分密钥轮换与恶意扫描

三条定阈值的纪律:必须带时间窗口(for: 3m),避免瞬时抖动误报;必须带流量下界(在线数 < 1000 时不报警),否则凌晨必误报;必须按实例或按机房分组,全局平均值等于没有告警。

大盘分层

层看什么判读
总览(一屏)在线数、msg/s、推送 P99、错误率、丢弃率5 秒内判断「有没有事」
网关分实例在线数、CPU、fd、协程、写队列定位单机异常与容量倾斜
客户端按平台与版本的重连次数、握手失败率定位客户端缺陷或发布影响
接入层101 响应码比例、upstream 超时、代理连接数区分接入层与应用层责任

日志与采样

  • 连接生命周期日志字段:conn_id、uid、gateway、remote_ip、握手耗时、断开原因、在线时长。
  • 慢推送日志:内部推送耗时超过 200ms 打一条,带 target、目标数、最慢网关。
  • 不要为每条消息打一行 info 日志:10 万在线连接即使 30 秒一次心跳也有 3.3k 行/秒,磁盘会先撑不住。
  • 指标标签名与日志字段名保持一致,排查时才能在指标与日志之间来回印证。

常见坑

坑后果正确做法
只监控在线数,不看断开原因知道掉线却不知道为什么disconnects_total 按 reason 拆分
把心跳计入消息量消息曲线变成心跳曲线,业务变化被掩盖指标按 type 区分业务与心跳
用 uid 做 label时间序列基数爆炸,监控系统自身故障高基数维度只进日志与链路
只对全局聚合告警单实例掉线 90% 无法触发按实例/机房分组并设置最小实例数
阈值写死绝对值业务增长后频繁误报,运维开始忽略基于基线、同比与百分比
忽略采集失败目标进程消失后曲线是「平的」,看着正常对 up == 0 与 scrape 失败单独告警
只算连接数不算用户数客户端重连一次就多算一条在线两者都采集,看比值是否异常

巡检项

  • 每周核对在线数、各网关连接数之和、路由表条目数、Redis 计数四方是否一致,差异超 1% 即查。
  • 每季度做一次告警演练:手动触发核心告警,验证通知真的能到人。
  • 所有大盘必须有「昨天同一时刻」对比线与发布事件标注。
  • 指标采集失败、日志写入失败、磁盘水位都必须有独立告警,静默故障最危险。

小结:在线数、连接质量、消息延迟、进程资源四类指标缺一不可——用 im_ws_online_connections 与 im_ws_connects_total{result} 区分「掉线」和「重连」,用 disconnects_total{reason} 定位掉线根因,把延迟拆成上行、内部、下行与心跳 RTT 分段测量;告警必须带时间窗口、流量下界与实例维度,并把高基数维度留在日志里,才能既看得见问题又不把监控系统自己压垮。

笔记加载中…