🔵 WebSocket 画布协作:神经系统

对讲机(连接)的一生:敲门 → 通话 → 广播 → 挂断销号。覆盖核心盘 🔵 组 7 张面板(c20–c26)· 后端核心仪表盘 siliang-backend。

用户 A · 浏览器页签 拖拽 · 编辑 · 光标移动 WS 对讲机 × 页签数 用户 B · 浏览器页签 和 A 同看一张画布 但连在另一台机器上 WS 握手 · 敲门检查 ① 鉴权 token ② 连接数上限 ③ Origin 校验 结果记入 outcome(c21) 机器 A · backend worker 登记会话 · 连接数 +1 收消息 ↑(c24) 心跳 presence:ping(c26) 挂断 → teardown 销号 机器 B · backend worker 登记会话 · 连接数 +1 收广播 → 推给 B(c25) 心跳 presence:ping(c26) 挂断 → teardown 销号 Redis pubsub 频道 跨机器广播站 A 发布 · B 订阅 broadcaster_pubsub 专用池 打满 = 广播发不出(m117) 事故 · 广播失明(c22) redis_pubsub > 0 持续 你在 A 机器画,B 机器的同伴 看不到 = 协作各自为政 正常挂断 · teardown 销号(c23) 连接结束 → 登记销号 → 时长入直方图 事故 · teardown_step 失败 → 幽灵 session 人走了登记还在:连接只涨不跌(c20) 敲门① 敲门② accepted 请进 accepted 请进 Redis 抖 → 此处断 ✕ 发布:A 改了这行 订阅推送 变更推给 B · 你画我看见 前端入口 backend worker Redis 广播站 事故路径 虚线 + ✕ = 事故传导 · 对讲机一生:敲门 → 通话 → 挂断销号

💡 一句话理解

WebSocket 就是对讲机:HTTP 是一问一答就挂电话,WebSocket 是接通后一直不挂、双方随时喊话。画布协作(你拖一个框、同伴立刻看到)全靠这 7 张面板盯着的“神经系统”。

这组图回答的只有一个问题:这条神经还通吗——连接接得进吗(c21)、消息传得动吗(c24/c25)、人还在吗(c26)、挂断挂得干净吗(c20/c22/c23)。

🧩 费曼拆解 讲给完全没接触过的小白

想象一间办公室,A 和 B 合伙画一张大图纸,每人手里一台对讲机。A 画了一笔,对着对讲机喊“我改了左上角!”;B 听到,立刻在自己那张图上照做——这就是 WebSocket 协作:连接一直是通的,谁有变更随时喊,不用像 HTTP 那样每次都重新拨号。

但办公室有两层楼(生产上是两台机器)。A 在 1 楼、B 在 2 楼,对讲机信号穿不了楼板。于是楼里装了个广播站(Redis pubsub):1 楼收到 A 的消息先广播给全楼,2 楼的对讲机再转告 B。广播站一坏,两人就“广播失明”——你画你的、我画我的,谁也看不见谁,这就是 redis_pubsub > 0 这条错误线的含义。

下班时要在前台登记簿上销号(teardown)。销号失败,登记簿上就留下“幽灵”:人走了,系统还以为他在——消息照发(发给空气)、名额照占(连接数只涨不跌)。这组 7 张图,盯的就是对讲机从敲门、通话到挂断销号的整条生命周期。

类比里的东西系统里对应的东西
对讲机WebSocket 长连接(siliang_ws_connections_active)。一人开两个页签就是两台对讲机,所以连接数 ≠ 人数。
敲门 + 门卫查证件WS 握手三道门:鉴权 token / 连接数上限 / Origin 校验,结果按 outcome 记在 siliang_ws_connections_total(c21)。
对讲机里喊“我改了这行”业务消息(不含心跳):接收是 c24(用户动作进服务器),发送是 c25(服务器广播给房间里其他人)。
每隔几秒喊“还在吗”心跳 presence:ping / cursor / select(c26),顺带上报“这个人的鼠标在动”。
楼内广播站Redis pubsub 跨实例广播。广播线出问题 = redis_pubsub > 0(c22),两台机器各自为政。
下班销号teardown 清理。销不干净 = 幽灵 session(c22 的 teardown_step)+ 时长 P99 异常长(c23)+ 活跃连接只涨不跌(c20)。

🛠 动手验证(在 Grafana 里亲手做一遍)

  1. 打开核心盘 🔵 组「WS 当前活跃连接数」——工作时段应是与「在线用户数」同节奏的几十到几百条(<100 绿);记住这个量级。
  2. 看「WS 连接结果分布」——预期 accepted 一枝独秀、其他拒绝线长期贴地;哪条拒绝线抬头,就是哪道门在拦人。
  3. 把「消息接收 QPS」和「消息发送 QPS」放在一起看——有人拖动画布时两条线应同步起伏:有“进话”就有“出话”。
  4. 对照「心跳 QPS」和活跃连接数——有心跳 = 有活人;如果连接还在而心跳归零,屋里全是“僵尸”。

🧠 必知必会 看懂本组 7 张图的地基

WebSocket = 对讲机
先通过一次 HTTP 握手“升级”成 WebSocket,此后是全双工长连接,双方随时互喊。代价是连接有状态:要心跳保活、要正确清理,否则就泄漏。
# HTTP: 一问一答就挂;WS: 接通后随时互喊
# 握手走一次 HTTP Upgrade,之后长连接全双工
连接 ≠ 用户
一人开多页签 = 多条连接;同一人跨 worker 还可能被算成两人(近似值)。对外报“多少人”用 c1 的去重口径,c20 只用于容量与泄漏判断。
sum(siliang_ws_connections_active{job="siliang_backend_worker"})
# 连接数 ≈ 在线用户数 × 人均页签数(约每人 1–2 条)
握手三道门
敲门之后有四种结果:accepted=请进;其余被拒之门外——鉴权失败(token 问题)/ 超限(撞连接数上限)/ Origin 不对(跨域配置)。
sum(rate(siliang_ws_connections_total[5m])) by (outcome)
# accepted 之外任何线抬头 → 对照 outcome 定位是哪道门在拦人
心跳(presence:ping)
客户端每隔几秒报“我还活着”,顺带报 cursor/select(光标在动)。心跳是“活人计数器”:心跳归零而连接还在 = 全是僵尸连接。
sum(rate(siliang_ws_heartbeat_messages_total[5m])) by (type)
# 与在线人数同节奏的稳定脉冲;归零 = 僵尸警报
pubsub 广播
多台机器之间同步消息全靠 Redis 发布订阅:用户 A 连机器 1、B 连机器 2,A 的变更由机器 1 发布到频道,机器 2 订阅到再推给 B。这是跨机器协作的命脉。
sum(rate(siliang_ws_errors_total[5m])) by (error_type)
# redis_pubsub > 0 = 广播失明:跨机器协作已断
幽灵 session
清理步骤(teardown)执行到一半 Redis 出故障,登记没销掉——系统以为有人在,其实人早走了。危害:占名额、消息发给空气、状态错乱。
# teardown_step 高 = Redis 故障正在制造幽灵 session
# 旁证:内存盘「WS 会话互校验」两线分叉(m120)
僵尸连接
人走了、连接没关——清理逻辑没触发,服务器还替它占着内存和名额,缓慢积累直到资源耗尽。时长 P99 异常长 + 活跃连接只涨不跌是两大信号。
histogram_quantile(0.99, sum(rate(siliang_ws_connection_duration_seconds_bucket[5m])) by (le))
# P99 异常长 = 有连接该断没断(与 c20 互证)
Counter 与 Gauge 口径
本组两种类型混用:_active 结尾是 Gauge(温度计,直接读数);_total 结尾是 Counter(里程表,必须配 rate 看速度)。写错口径曲线就废了。
# _active(连接数)= Gauge,直接 sum 读数
# _total(连接/消息/心跳数)= Counter,必须 rate()
“在线”的三种口径
别把“在线”混为一谈:① 画布 WS 在线(本组的连接/会话)② 聊天 SSE 在线(c2 的监听数)③ 全能/生图/视频的 SSE 不在这两个数里——那部分要看 HTTP 在途与 LLM 并发。跨组对比前先对口径。
# 画布协作的“在线” → siliang_ws_*(本组)
# 聊天的“在线”     → siliang_chat_resume_active_*(c2/c6)
“不含心跳”口径
c24/c25 只统计业务消息(拖拽/编辑),心跳单独算在 c26。判断“协作活跃度”用业务消息,判断“有多少活人”用心跳——两件事别混。
# 活跃度两问:
# 谁在做什么动作 → messages_received by (type)(c24)
# 还有多少活人   → heartbeat_messages by (type)(c26)

📋 逐面板精讲 7 张图一张不落

🔵 c20 · WS 当前活跃连接数

它是什么:画布协作此刻有多少条“对讲机”在线——所有 worker 上活跃 WebSocket 连接的总和,Gauge 直接读数。它和「在线用户数」是近亲:用户数按人去重,连接数不去重,所以一人开两个页签就是两条连接。

回答什么问题:现在有多少条活连接在服务?连接有没有只涨不跌(泄漏/僵尸堆积)?

✅ 与在线用户数成比例(约每人 1–2 条),随业务节奏起伏;<100 绿 / 100–500 黄。

🚨 持续上涨不回落 = 连接泄漏(断了没回收)——先看 c23 时长 P99 和 c22 的 teardown_step 互证,再对照发布记录;100–500 之间就该警惕,别等爆了才看。

# 面板 PromQL(照抄主图谱口径)
sum(siliang_ws_connections_active{job="siliang_backend_worker"})

联动:主图谱 c20 ↗ · 在线用户数 c1(人数口径) · 相关课程:在线用户与协作规模 / WS 会话内存

🔵 c21 · WS 连接结果分布

它是什么:敲门之后的结果分布,按 outcome 拆成几条线:accepted=请进;其他是被拒之门外的各种原因——鉴权失败 / 超限 / Origin 不对。是 Counter,用 rate 看每秒速率。

回答什么问题:用户反馈“连不上画布”时,到底是哪道门把他拦住了?

✅ accepted 一枝独秀,其他拒绝线长期贴地。

🚨 哪种拒绝高就是哪类问题:鉴权失败高 = token 问题(前端或网关);超限高 = 撞连接数上限(先查 c20 是不是泄漏占满名额);Origin 高 = 跨域配置问题(多半伴随发布/域名变更)。

# 面板 PromQL(照抄主图谱口径)
sum(rate(siliang_ws_connections_total[5m])) by (outcome)

联动:主图谱 c21 ↗ · 活跃连接数 c20(超限时先看它) · 相关课程:5xx vs 4xx / HTTP 健康

🔵 c22 · WS 错误类型分布

它是什么:协作神经系统的“病变类型”分布,按 error_type 拆开。两类重点病变:teardown_step 高 = Redis 故障留下“幽灵 session”(人走了系统以为还在);redis_pubsub > 0 = 跨实例广播失明——你在 A 机器画,B 机器上的同伴看不到。

回答什么问题:协作出问题的时候,病在哪条神经上?是挂断销号坏了,还是广播线断了?

✅ 长期贴地。

🚨 redis_pubsub 持续 > 0 = 广播失明,用户可感知的“协作失灵”,立即排查 Redis 健康与广播池(内存盘 m117 的 broadcaster_pubsub 池);teardown_step 高 = 幽灵 session 在堆积,对照 m120 会话互校验。

# 面板 PromQL(照抄主图谱口径)
sum(rate(siliang_ws_errors_total[5m])) by (error_type)

联动:主图谱 c22 ↗ · Redis 广播池 m117 · 相关课程:连接池台账 / WS 会话内存

🔵 c23 · WS 连接持续时长 P50/P95/P99

它是什么:一条“对讲机”从接通到挂断平均开多久(直方图插值出 P50/P95/P99 分位数)。连接总要结束——用户关页面、超时被踢——时长分布能暴露“该死的没死”。

回答什么问题:连接的生死轮回正常吗?有没有挂不掉的僵尸连接?

✅ 分位数形态稳定:P50 反映典型使用时长,P99 不异常拉长、不随时间持续爬升。

🚨 P99 异常长 = 僵尸连接(teardown 清理没触发)——与 c20 活跃连接只涨不跌互证,去查 teardown 逻辑与 Redis 健康。

# 面板 PromQL(照抄主图谱口径)
histogram_quantile(0.50/0.95/0.99, sum(rate(siliang_ws_connection_duration_seconds_bucket[5m])) by (le))

联动:主图谱 c23 ↗ · 错误类型 c22(幽灵旁证) · 相关课程:P50/P95/P99 / 直方图水桶

🔵 c24 · WS 业务消息接收 QPS(不含心跳)

它是什么:↑ 用户在画布上做的每个动作(拖拽/编辑…)传到服务器的速度,按 event type 拆开。注意口径是“不含心跳”——心跳单独算在 c26,这里只看真正的业务动作。

回答什么问题:协作活跃度如何?哪种操作最频繁(决定优化优先级)?

✅ 跟业务节奏走,工作时段起伏、深夜落回低位;与发送 QPS(c25)同步呼吸。

🚨 接收突然归零 = 用户侧断连或接入层故障(对照 c21 拒绝线和 c20 连接数);无活动时段却暴涨 = 异常客户端/脚本在刷。

# 面板 PromQL(照抄主图谱口径)
sum(rate(siliang_ws_messages_received_total[5m])) by (type)

联动:主图谱 c24 ↗ · 发送 QPS c25(成对看) · 相关课程:rate() 算速度

🔵 c25 · WS 业务消息发送 QPS(不含心跳)

它是什么:↓ 服务器把变更广播给房间里其他人的速度。接收是“进话”,发送是“出话”,一对进出要放在一起看才有意义。

回答什么问题:广播压力多大?广播链路(pubsub)还通吗?

✅ 与接收 QPS 同起伏——有人进话就有人出话,比例稳定。

🚨 接收正常而发送归零 = 广播链路(pubsub)出问题——马上看 c22 的 redis_pubsub 线和内存盘 m117 广播池是否打满。

# 面板 PromQL(照抄主图谱口径)
sum(rate(siliang_ws_messages_sent_total[5m])) by (type)

联动:主图谱 c25 ↗ · 广播失明 c22 · 相关课程:连接池台账

🔵 c26 · WS 心跳消息 QPS(客户端活跃度)

它是什么:客户端每隔几秒报一次“我还活着”(presence:ping)和“我的光标在这”(cursor/select)。这是“活人”的直接计量——连接可以骗人,心跳不会。

回答什么问题:有多少“活人”在动?连接数里的水分有多大?

✅ 与在线人数同节奏的稳定脉冲;按 type 拆开各条线比例稳定。

🚨 心跳归零但连接还在 = 全是僵尸连接——对照 c20 若同时只涨不跌,立即排查 teardown 清理链路。

# 面板 PromQL(照抄主图谱口径)
sum(rate(siliang_ws_heartbeat_messages_total[5m])) by (type)

联动:主图谱 c26 ↗ · 活跃连接数 c20(僵尸互证) · 相关课程:rate() / 在线用户口径

🏭 生产实战 real world

场景 1 · 用户反馈“画布协作不同步,我画了他看不见”

典型的“广播失明”投诉。别急着重启,三步定位:先确认是不是广播线断了,再确认是单机问题还是 Redis 整体不稳,最后查广播池有没有被打满。

# 第 1 步:先看是不是“广播失明”(跨机器协作断)
sum(rate(siliang_ws_errors_total{error_type="redis_pubsub"}[5m]))
  # > 0 即实锤:Redis pubsub 广播线出问题,两台机器各自为政

# 第 2 步:按 instance 拆——单机失明还是 Redis 整体抖?
sum(rate(siliang_ws_errors_total{error_type="redis_pubsub"}[5m])) by (instance)
  # 只有一台机器报 = 那台的 pubsub 订阅断了
  # 两台都报 = Redis 本身在抖,去查 Redis 健康

# 第 3 步:看广播专用池是不是被打满(内存盘)
max_over_time(siliang_redis_pool_connections{state="in_use", pool="broadcaster_pubsub"}[1m])
  # 贴着 capacity = 广播借不到连接,消息发不出(对照 m117 使用率卡)

判读:redis_pubsub 回到 0 且发送 QPS(c25)恢复与接收同步起伏,才算修好。

场景 2 · 深夜告警:WS 活跃连接数爬升不回落(怀疑僵尸)

连接数只涨不跌有两种可能:真热闹,或全是尸体。用“僵尸三联证”分辨:心跳、时长 P99、teardown 错误。

# 1) 确认形态:曲线是不是只涨不跌
sum(siliang_ws_connections_active{job="siliang_backend_worker"})

# 2) 三联证之一:心跳还有没有(活人才发心跳)
sum(rate(siliang_ws_heartbeat_messages_total[5m]))
  # 连接在涨而心跳平平 = 新连接是真、旧连接是尸

# 3) 三联证之二:时长 P99 是不是异常长
histogram_quantile(0.99, sum(rate(siliang_ws_connection_duration_seconds_bucket[5m])) by (le))
  # P99 拉长 = teardown 清理没触发,“该死的没死”

# 4) 幽灵旁证:teardown_step 错误速率
sum(rate(siliang_ws_errors_total{error_type="teardown_step"}[5m]))
  # > 0 = Redis 故障正在制造幽灵 session(人走了登记还在)

场景 3 · 一堆用户突然连不上画布(握手被拒)

“连不上”不是一种病,是四种。按 outcome 拆开,每种拒绝指向完全不同的病灶和负责人。

# 按 outcome 拆开看是哪道门在拦人
sum(rate(siliang_ws_connections_total[5m])) by (outcome)
  # 鉴权失败高 = token 过期/签发问题(前端或网关侧)
  # 超限高   = 撞连接数上限:先看 c20 是不是泄漏把名额占满
  # Origin 高 = 跨域配置问题(多半伴随发布/域名变更)

# 怀疑“超限”时的快速验证:对照活跃连接数绝对值
sum(siliang_ws_connections_active{job="siliang_backend_worker"})
  # 若同时贴高位 → 大概率是泄漏占满名额,而不是真有那么多人
  # 再用心跳 QPS 验水分:心跳没涨 = 名额被尸体占着

场景 4 · 发布后确认 WS 平滑切换

滚动发布时每个旧 worker 要把连接干净地交出去。逐 instance 看连接排空,发布后才算真正收工。

# 滚动发布时逐 worker 看:旧 instance 连接应依次排空归零
sum(siliang_ws_connections_active{job="siliang_backend_worker"}) by (instance)
  # 每个旧 instance 依次跌到 0 = 干净退场
  # 卡在半空不归零 = 排空卡住(对照 HTTP 并发 c18 的 ops 排空口径)

# 发布后协作功能回归:收发两条线应恢复同起伏
sum(rate(siliang_ws_messages_received_total[5m])) by (type)
sum(rate(siliang_ws_messages_sent_total[5m])) by (type)
  # 有进有出 = 广播链路健康;发送归零 = pubsub 没接好

场景 5 · 给 WS 僵尸连接加一条防回退告警

僵尸连接靠人盯图发现不了早期形态。用“连接爬升 + 心跳不涨”的组合条件做告警,让它在堆积早期就叫。

# 告警:连接斜率向上 且 心跳没有同步增长 = 疑似僵尸堆积
#(示例规则,阈值按业务节奏校准后启用)
- alert: SiliangWSZombieSuspicion
  expr: |
    deriv(sum(siliang_ws_connections_active{job="siliang_backend_worker"})[1h:5m]) > 0
    and
    sum(rate(siliang_ws_heartbeat_messages_total[5m]))
      < sum(rate(siliang_ws_heartbeat_messages_total[5m] offset 1h))
  for: 15m
  labels:
    severity: warning
  annotations:
    summary: "WS 连接爬升但心跳未同步:疑似僵尸连接堆积,查 c23 P99 与 teardown_step"

判读:告警触发后先看 c23 的 P99——它拉长就是清理逻辑没跑,再看 c22 teardown_step 找 Redis 故障的痕迹。

⚠️ 常见坑 pitfalls

坑 1 · 拿连接数当在线人数汇报 — 症状:周报里“在线 800 人”其实只有 400 人。原因:一人开多页签 = 多条连接,连接数不去重;同一人跨 worker 还会被算成两人。正解:对外报人数用 c1 去重口径,连接数只用于容量与泄漏判断。
# 错: sum(siliang_ws_connections_active)   # → 当“在线人数”发周报
# 对: sum(siliang_ws_online_users{job="siliang_backend_worker"})  # → 按 user_id 去重
坑 2 · 连接数一涨就喊泄漏 — 症状:连接 100→300 就拉群说泄漏了。原因:可能只是用户真的变多。正解:对照心跳与在线用户是否同比例上涨,心跳不涨而连接涨才是僵尸。
# 错: # 只看连接绝对值就下结论
# 对: sum(rate(siliang_ws_heartbeat_messages_total[5m]))  # → 对照心跳验水分
坑 3 · 对 Gauge 求 rate — 症状:连接数曲线变成一堆无意义的毛刺。原因:_active 是温度计(Gauge),直接读数;对它求速率没有物理意义。正解:Counter 配 rate,Gauge 直接读。
# 错: rate(siliang_ws_connections_active[5m])              # → 对 Gauge 求 rate,无意义
# 对: sum(siliang_ws_connections_active{job="siliang_backend_worker"})  # → 直接读数
坑 4 · 把心跳算进协作活跃度 — 症状:明明没人干活,“消息 QPS”却很高。原因:c24/c25 口径明确“不含心跳”,心跳单独在 c26;混着看活跃度会严重虚高。正解:活跃度用业务消息,“活人计数”用心跳。
# 错: # 用 messages_received 判断“有多少人在动” → 混入了心跳,虚高
# 对: sum(rate(siliang_ws_heartbeat_messages_total[5m])) by (type)  # → 心跳口径看活人
坑 5 · redis_pubsub 单点非零就当大事故 — 症状:图上一个孤立毛刺就拉全员排查。原因:Redis 短促抖动会自愈,单点非零不是失明。正解:看持续性——持续 > 0 且发送 QPS 同步塌陷,才是真广播失明。
# 错: # 单点 redis_pubsub > 0 → 立即定级 P1
# 对: # 持续 > 0 + 发送 QPS 归零 → 才是广播失明,再按场景 1 下钻
坑 6 · 只盯 P50 判断连接健康 — 症状:P50 一直平稳,僵尸却堆了几百条。原因:僵尸是少数派,P50 看不见,P99 才暴露“该死的没死”。正解:僵尸判断看 P99 + 活跃连接形态,双证齐全再动手。
# 错: histogram_quantile(0.50, …)   # → P50 平稳就放心
# 对: histogram_quantile(0.99, sum(rate(siliang_ws_connection_duration_seconds_bucket[5m])) by (le))
坑 7 · 分位数忘了 by (le) — 症状:P99 曲线变成乱码或永远为零。原因:直方图分位数必须按 le(水桶边界)聚合后插值。正解:sum by (le) 是 histogram_quantile 的固定前缀。
# 错: histogram_quantile(0.99, rate(siliang_ws_connection_duration_seconds_bucket[5m]))
# 对: histogram_quantile(0.99, sum(rate(siliang_ws_connection_duration_seconds_bucket[5m])) by (le))
坑 8 · 广播失明只会重启 backend — 症状:redis_pubsub 报错就滚动重启 backend,过两天又犯。原因:pubsub 是 Redis 的功能,Redis 抖或广播池打满才是根因。正解:先看 m117 broadcaster_pubsub 池与 Redis 本身健康,再谈重启。
# 错: # 症状: redis_pubsub > 0 → 直接滚动重启 backend
# 对: # 正解: 先查 broadcaster_pubsub 池使用率(m117)与 Redis 健康
坑 9 · 把握手拒绝当服务端 5xx 翻后端日志 — 症状:用户连不上,后端日志却一片干净。原因:鉴权失败/超限/Origin 是握手门拦人,请求根本没进业务代码。正解:先看 outcome:三种拒绝各指向 token / 上限 / 跨域配置,方向完全不同。
# 错: # 症状: 连不上 → 翻 backend 异常日志半天
# 对: sum(rate(siliang_ws_connections_total[5m])) by (outcome)  # → 先定哪道门拦的

🎓 费曼自测 合上书,能讲出来才算懂

Q1 · 用户说“我画的同伴看不见”,你按什么顺序看哪几张图?

参考答案:先看 c22 错误类型分布——redis_pubsub 持续 > 0 就是广播失明(两台机器各自为政);再看 c25 发送 QPS 是否与 c24 接收同步,发送归零 = 广播链路断了;然后按 instance 拆错误线分辨单机订阅断还是 Redis 整体抖;最后查内存盘 m117 的 broadcaster_pubsub 池是否打满。全链路顺序和主图谱排查速查表“画布协作不同步”一行一致。

Q2 · 连接数(c20)和在线用户数(c1)有什么区别?为什么说连接数不是人数?

参考答案:c1 把所有 worker 上的 WS 会话按 user_id 去重后求和,是“多少人”;c20 是活跃连接总数,不去重——一人开两个页签就是两条连接,同一人跨 worker 还会被算成两人。所以连接数 ≈ 人数 × 人均页签数(约每人 1–2 条),它适合做容量和泄漏判断,不适合当“人数”汇报。

Q3 · 什么是幽灵 session?它和僵尸连接是什么关系?分别在哪些图上露马脚?

参考答案:幽灵 session 是“人走了、登记还在”——teardown 清理执行到一半 Redis 出故障,会话登记没销掉,系统还给他发消息、占名额。僵尸连接是更广的说法:连接没被正确关闭,服务器还替它占资源。露马脚的地方:c22 的 teardown_step 错误线、c23 时长 P99 异常长、c20 活跃连接只涨不跌、内存盘 m120 会话互校验两线分叉(注册表里有尸体)。

Q4 · 为什么 c24/c25 要标“不含心跳”?心跳单独一张图(c26)能回答什么别的问题?

参考答案:心跳每隔几秒自动发一次,量级大且与“用户在做什么”无关;混进业务消息会把协作活跃度严重虚高。c26 回答的是“有多少活人”:活人才发心跳,心跳归零而连接还在 = 屋里全是僵尸。所以 c24/c25 回答“协作活跃度与广播压力”,c26 回答“连接里的水分”——三张图一起才能既看热闹又验人头。

← 上一课:🟢 HTTP 健康:门面四件套 📚 课程目录 下一课:🟠 计费归因与灵感检索 →