"现在有多少活人、多少房间"——三种"在线"口径别混,按 user_id 去重才是"人数",跨 worker 是近似值。覆盖核心盘 🟣 组 6 张面板(c1–c6)· 后端核心仪表盘 siliang-backend(job=siliang_backend_worker · 刷新 15s · 时间窗 6h)。
把 backend 想成一个游乐场:c1 的大数字是"此刻操场上有多少个不同的学生"(按学生证 user_id 去重),c3 是"有多少间教室里还有人"(有活跃 WS 会话的画布房间)。它们都是 Gauge(温度计),直接读数,不累积。
为什么重要?它是整个仪表盘的"体温计之首":产品火不火、发布后用户有没有掉光、故障波及多少人,第一眼看的就是它。但先记住两个口径陷阱:同一人跨 worker 会被算成两人(近似值);全能/生图/视频的 SSE 不在这两个数里。
想象你是游乐场的园长,想知道"现在园里有多少人"。门口闸机不记名,只数"刷了几次卡"——张三上午进园一次,同伴帮他又刷了一次,闸机就把他数成两个人。backend 数在线用户也是这样:每个 worker 只看得见自己手里的 WebSocket 会话,它不知道"253-backend-0 上的那条会话"和"253-backend-2 上的那条"是不是同一个人。所以按 user_id 去重之后,跨 worker 的同一个人仍可能被算成两人——这就是口径卡里写的"近似值"三个字的来历。
再看"人数"和"电话线"的区别。聊天室里有两个数:"几个人"(去重用户,一人开 5 个页签只算 1)和"几条电话线"(在途 SSE 连接数,每个页签一条)。WebSocket 是对讲机(画布协作,双向喊话),SSE 是收音机(聊天推流,单向播)。收音机开几台,不代表有几个人在听。健康状态下,连接数 ≈ 人数 × 人均页签数,比例稳定;如果连接数独自一路涨、人数不动,那就是"收音机开了没人关"——监听生成器泄漏,挂掉的 SSE 没人挂断。
最后是"在线"的三种口径(说明卡 c4 的内容):① 画布 WS 在线;② 聊天 SSE 在线;③ 全能/生图/视频的 SSE——不在这两个数里,想看它们得去 HTTP 在途(c18)和 LLM 并发(c14)。数错口径,结论就会南辕北辙:把生成类用户算进"在线人数",会让 c1 看起来"虚低";反过来拿 c1 回答"有多少人在等生图",又永远对不上。
| 类比里的东西 | 系统里对应的东西 |
|---|---|
| 操场上不同的学生数 | c1 在线用户数:所有 worker 上的 WS 会话按 user_id 去重后求和——"去重"管一人多页签,"求和"管 4 个 worker |
| 一人被闸机刷了两次 | 同一用户跨 worker 各持一条会话被算成两人——所以 c1 是近似值,看趋势别做考勤 |
| 开着没人关的收音机 | 挂了没人挂断的 SSE 连接——c2 连接数持续上涨不回落 = 监听生成器泄漏 |
| 有人待着的教室数 | c3 在线协作画布数:有活跃 WS 会话的 workspace 数,衡量协作广度,阈值 100/300 |
| 闸机只认本入口 | 每个 worker 只数自己的会话,sum(...) 跨 worker 求和才是大盘;只信 job=siliang_backend_worker |
| 闭园广播(人全走了) | c1 归零或骤降 = 接入层 / WS 出问题——先看大数字定级,再下钻趋势线 |
<50 绿 / 50–200 黄 / >200 红,对"现在多少人"心里有个数。rate()。# 对:直接读此刻的数(Gauge 不配 rate) sum(siliang_ws_online_users{job="siliang_backend_worker"})
# 近似:跨 worker 的同一人可能被算成 2 人(口径卡原文) sum(siliang_ws_online_users{job="siliang_backend_worker"})
# 生成类流量不在在线数里,要看在途:
sum(siliang_http_requests_in_progress) by (method)# 聊天室:人数与连接数(面板口径照抄)
sum(siliang_chat_resume_active_listener_users)
/ sum(siliang_chat_resume_active_listeners)# 不带过滤 = 可能混进双计的 server job {job="siliang_backend_worker"} # 唯一可信口径
# 大盘 = 4 个 worker 相加(c3 口径) sum(siliang_ws_workspaces_active{job="siliang_backend_worker"})
# Counter 配 rate,按 outcome 拆(c6 口径) sum(rate(siliang_chat_resume_listener_outcome_total[5m])) by (outcome)
# 六条趋势线之一:pubsub 订阅任务 sum(siliang_ws_pubsub_tasks_active) # 独自爬升 = 在堆积
它是什么:核心盘的第一张大数字:此刻有多少人"坐在画布前"。实现上是把 4 个 worker 手里的 WebSocket 会话按 user_id 去重后求和——"去重"意味着一人多页签只算一人,"求和"意味着它是 4 个 worker 各自报数的合计。它是 Gauge:随业务节奏起伏,不累积。
回答什么问题:产品的实时热度如何?发布/故障后用户是不是掉光了?
✅ 随业务节奏起伏;背景色 <50 绿 / 50–200 黄 / >200 红
🚨 归零或骤降 = 接入层/WS 出问题;注意同一用户跨 worker 会被算成两人,这是近似值。动作:先看 c5 趋势确认是"掉光"还是"正常回落",再转 WS 组(c20–c22)定位。
# 面板 PromQL(照抄主图谱口径) sum(siliang_ws_online_users{job="siliang_backend_worker"})
联动:主图谱 c1 ↗ · 相关课程:Counter vs Gauge · WebSocket 画布协作
它是什么:聊天室的两个数并排看:"几个人"(去重用户,一人开 5 个页签只算 1)和"几条电话线"(在途 SSE 连接数,每个页签一条)。SSE 是服务器单向推流的收音机,断线可从上次位置续听——四两的 chat_resume(断线续听)名字就是这么来的。
回答什么问题:聊天功能有多少真人在线?连接数和人数比例正常吗?
✅ 连接数 ≈ 人数 × 人均页签数,波动正常;阈值 100/400
🚨 连接数持续上涨不回落 = 监听生成器泄漏(挂了的 SSE 没人挂断)。动作:把窗口拉到 6h 确认"只涨不跌",再去 c6 看挂断原因分布找证据。
# 面板 PromQL(照抄主图谱口径:人数 / 连接数)
sum(siliang_chat_resume_active_listener_users)
/ sum(siliang_chat_resume_active_listeners)
联动:主图谱 c2 ↗ · 相关课程:WebSocket 画布协作 · 后台生成任务(泄漏同源思想)
它是什么:有多少个"画布房间"里还有人——有活跃 WS 会话的 workspace 数。人数(c1)衡量"来了多少人",画布数衡量"协作散布在多广":10 个人挤在同一间房,和 10 个人各开各的房,业务意义完全不同。
回答什么问题:协作发生的房间数——衡量真实协作广度。阈值 100/300。
✅ 随业务节奏起伏,与 c1 人数同涨同跌、比例稳定;面板内置 100/300 参考阈值
🚨 房间数与人数的比例突变(房间涨、人不涨)说明协作形态变了,值得结合 c5 六条线一起看;两数齐跌则是真实流量下降
# 面板 PromQL(照抄主图谱口径) sum(siliang_ws_workspaces_active{job="siliang_backend_worker"})
联动:主图谱 c3 ↗ · 相关课程:WebSocket 画布协作 · job / instance / label
它是什么:在线组的说明书,写清两件事。第一,"在线"有三种,别混:①画布 WS 在线 ②聊天 SSE 在线 ③全能/生图/视频的 SSE——不在这两个数里(要看 HTTP 在途和 LLM 并发)。第二,为什么只信 job=siliang_backend_worker:共享端口的 server job 随机命中 worker、会双计数,已从所有面板排除。
回答什么问题:看数之前先问"这个数是怎么数的、没数谁"——口径不清,后面全白看。
✅ 说明卡没有健康态——它的"健康" = 你看懂口径之后再去看其余五张图
🚨 最大的危险是拿口径②的数回答口径①的问题,或者忘加 job 过滤把双计数的 server job 加进来
# 口径卡的核心:所有面板统一排除 server job sum(siliang_ws_online_users{job="siliang_backend_worker"}) # 生成类 SSE 不在在线数里:去 c18(HTTP 在途)/ c14(LLM 并发)看
联动:主图谱 c4 ↗ · 相关课程:job / instance / label · HTTP 健康(第三种口径去哪看)
它是什么:把在线组所有"温度计"画成 6 条时间曲线:去重用户、聊天用户、在线画布、WS 会话总数、pubsub 订阅任务、聊天 SSE 监听。如果说 c1/c2/c3 是照片,这张就是录像——大数字只告诉你"现在多少",曲线才告诉你"往哪走"。
回答什么问题:一天的业务节奏长什么样?增长还是流失?发布前后对比如何?
✅ 曲线平滑起伏、彼此比例稳定
🚨 某条线脱离大部队独自爬升 = 对应资源在堆积(如 pubsub 任务)。动作:顺着那条线找所属模块下钻,先确认堆积源头再谈处理。
# 六条曲线的取数(主图谱口径展开) sum(siliang_ws_online_users) # 去重用户 sum(siliang_ws_workspaces_active) # 在线画布 sum(siliang_ws_sessions_active) # WS 会话总数 sum(siliang_ws_pubsub_tasks_active) # pubsub 订阅任务 sum(siliang_chat_resume_active_listener_users) # 聊天用户 sum(siliang_chat_resume_active_listeners) # 聊天 SSE 监听
联动:主图谱 c5 ↗ · 相关课程:WebSocket 画布协作 · max_over_time 与 deriv
它是什么:每条聊天"电话线"挂断时都会记一个原因(outcome):completed=正常听完;disconnected=用户直接关页面(大多数,正常);lease_expired=worker 租约失效后收敛退出;error=读流出异常。outcome 是 Counter,配 rate 按 outcome 拆开,就是"挂断方式"的实时分布。
回答什么问题:SSE 链路健壮吗?
✅ disconnected + completed 占绝对多数
🚨 error 频繁 = 上下行不稳;lease_expired 高 = 实例在异常退出。动作:error 高先查网络与上下游;lease_expired 高对照发布记录与 c11(跨进程收尸)。
# 面板 PromQL(照抄主图谱口径) sum(rate(siliang_chat_resume_listener_outcome_total[5m])) by (outcome)
联动:主图谱 c6 ↗ · 相关课程:后台生成任务(租约/收尸机制) · rate():算速度
滚动发布会断开旧连接,在线数抖一抖是正常的;要抓的是"掉下去不回来"。
# 发布窗口前后对比在线人数(Gauge 直接读,不配 rate) sum(siliang_ws_online_users{job="siliang_backend_worker"}) # 健康形态:发布瞬间小幅回落(旧连接断开重连),1–2 分钟回到原水位 # 危险形态:掉下去不回来 —— 接入层/WS 出问题,接着往下看 # c5 六条线逐条展开,找"谁先掉的" sum(siliang_ws_online_users) # 去重用户 sum(siliang_ws_workspaces_active) # 在线画布 sum(siliang_ws_sessions_active) # WS 会话总数 sum(siliang_chat_resume_active_listeners) # 聊天 SSE 监听(电话线) # 判读:全部同跌 = 接入层/网络;只有 WS 线掉 = WS 专属问题(转 c20–c22)
判读收尾:几分钟内回到原水位 = 发布无恙;持续低迷 = 按上面的分支下钻。
收音机开了没人关——挂掉的 SSE 没人挂断,连接数就会一路涨而人数不动。
# 两个数分开看(c2 口径) sum(siliang_chat_resume_active_listeners) # 几条线(阈值 100/400) sum(siliang_chat_resume_active_listener_users) # 几个人(去重) # 挂断原因分布,找"该挂没挂"的证据(c6 口径) sum(rate(siliang_chat_resume_listener_outcome_total[5m])) by (outcome) # error 频繁 = 读流出异常,上下行不稳 # lease_expired 高 = worker 租约失效被收敛退出(实例问题,转 grp-bg-tasks) # 临门一脚:窗口拉到 6h,连接线只涨不跌 = 泄漏实锤
判读收尾:人数平稳 + 连接线单调爬升 = 泄漏,立刻报给值班开发。
两个数对不上时,先查口径再查 bug——九成是口径问题,不是数据坏了。
# 第 1 步:有没有混进 server job(双计元凶,口径卡 c4) sum(siliang_ws_online_users) # 错:不带过滤,混入双计 sum(siliang_ws_online_users{job="siliang_backend_worker"}) # 对:唯一可信口径 # 第 2 步:剩下的差值多半是跨 worker 同人双算(近似值,正常现象) # 第 3 步:用会话总数交叉验证量级 sum(siliang_ws_sessions_active) # 未去重口径,应 ≥ 去重人数 # 判读:差值 ≈ 跨 worker 双算量级 = 正常;差值巨大 = 查注册表泄漏(内存盘 m120)
主图谱排查速查表里"每日例行巡检"的第一站就是在线组——便宜、快、信息量大。
# 每日巡检(主图谱 #triage「每日例行巡检(2 分钟版)」口径) sum(siliang_ws_online_users{job="siliang_backend_worker"}) # c1:和昨天同时段比 sum(siliang_chat_resume_active_listeners) # c2:电话线在正常区间? # 判读:与昨日同时段同量级 = 过关 # 骤降/归零 = 按「场景 1」的路径下钻,先接入层后 WS
增长还是流失?把时间窗切到 7d,三条核心线就是现成的周报素材。
# 周报配图:7d 窗口下的核心三条线(c5 口径子集) sum(siliang_ws_online_users) # 去重用户 —— 热度 sum(siliang_ws_workspaces_active) # 在线画布 —— 协作广度(阈值 100/300) sum(siliang_chat_resume_active_listener_users) # 聊天用户 —— 粘性 # 判读:三条线同步上台阶 = 真增长 # 只有画布涨、用户不涨 = 空房间变多,协作在退潮
# 错: rate(siliang_ws_online_users[5m]) # Gauge 求 rate,无意义 # 对: sum(siliang_ws_online_users{job="siliang_backend_worker"})
# 错: 拿 c1 的大数字和业务后台逐人核对 # → 永远对不齐 # 对: sum(siliang_ws_sessions_active) # → 会话口径交叉验证
# 错: 用 c2 回答"全站多少人在线" # 对: sum(siliang_http_requests_in_progress) by (method) # → 生成类看在途
# 错: 拿 listeners 当用户数汇报 # 对: users = sum(siliang_chat_resume_active_listener_users) # → 去重人数
# 错: 只盯 c1 当前值 # 对: sum(siliang_ws_pubsub_tasks_active) # → 六条趋势线一起看(c5)
# 错: sum(siliang_ws_online_users) # → 混入双计 # 对: sum(siliang_ws_online_users{job="siliang_backend_worker"})
# 错: 看到 disconnected 高就报警 # 对: 只盯 error(上下行不稳)与 lease_expired(实例异常退出)两条线
# 错: lease_expired 高 → 查 SSE 代码 # 对: sum(rate(siliang_chat_resume_stale_finalized_total[5m])) # → 收尸计数互证
参考答案:把 4 个 worker 手里的 WebSocket 会话按 user_id 去重后求和。近似在两点:每个 worker 只看得见自己的会话,同一用户跨 worker 各持一条会被算成两人;而且去重只能对"报上来的数"做。所以它适合看趋势和量级,不适合做精确考勤。
参考答案:人数 = 去重用户(一人多页签只算 1);电话线 = 在途 SSE 连接数(每页签一条)。健康时连接数 ≈ 人数 × 人均页签数,比例稳定。连接数持续上涨而人数不动 = 监听生成器泄漏——收音机开了没人关,挂了的 SSE 没人挂断。
参考答案:不能。口径卡(c4)写明"在线"有三种:画布 WS 在线、聊天 SSE 在线、全能/生图/视频的 SSE——第三种不在这两个数里。要看生成类流量,去 c18(HTTP 在途请求)和 c14(LLM 并发调用数)。拿错口径的数回答问题,结论必然跑偏。
参考答案:第一步看 c5 六条趋势线,确认是"全部同跌"还是"某条先掉";第二步分支:全部同跌查接入层/网络,只有 WS 线掉转 c20–c22(连接数、连接结果、错误类型);第三步看 c6 的 outcome 分布——error 高是链路不稳,lease_expired 高是实例异常退出,对照发布记录。整个过程先定性(掉没掉)再定位(掉在哪)。