🟢 biz 业务后端:核心盘精简镜像

你在核心盘学过的每个体检项目,biz 这张盘原样再量一遍——看懂了核心盘,这 11 张图全部秒懂。覆盖 biz 盘 11 张面板(b0–b10)· 业务后端仪表盘 siliang-biz(job=siliang-biz · 刷新 30s)。

同一套体检项目,换一个服务再量一遍 biz 盘(siliang-biz · 11 图)= 核心盘 + 内存盘(61 图)的精简镜像 · 学法:记住映射表 + biz 特有三件事 镜像轴 同款 同款 同款 核心盘 · HTTP 四件套(你已学过) QPS c16 · 5xx c17 · 并发 c18 · P99 Top10 c19 job=siliang_backend_worker · 刷新 15s 5xx > 1 req/s → 按 handler 直奔日志 核心盘 · 连接池两对卡(m115–m118) DB 池:sync 30 / async 80 / checkpointer 30 Redis 池:shared 上限 50 + 广播池 broadcaster_pubsub 使用率卡看趋势 · 绝对值卡看离天花板多远 内存盘 · RSS 与 GC(m101 / m126) RSS 三形态:锯齿=健康 · 平台期=常态 · 爬升=泄漏 GC:gen2 停滞 + 待回收爬升 = 大对象驻留 deriv(RSS[1h]) > 5MB/h = 持续累积(告警口径) biz 盘 · HTTP 四件套(同款) QPS b1 · 5xx b2 · 并发 b3 · P95 Top5 b4 job=siliang-biz · 刷新 30s · handler 不含 /api/v2 要 P99:进查询把 0.95 改成 0.99 biz 盘 · 连接池两对卡(b5–b8) DB 池:10 + overflow 20 = 30(每 worker 一份) Redis 池:max_connections=50,与 Agent 服务共享 池面板按机器 repeat 成对出现 · 下钻用 HOST 变量 biz 盘 · 内存与 GC(b9 / b10) b9:每台机器一张图、每 worker 一条 RSS 线 b10:gen2 停滞而 gen0/1 活跃 = 大对象驻留 只反映被 Prometheus 抓到的 worker(口径卡 b0) 健康路 · 借了就还 b5 使用率有涨有落(< 80%)· b6 离上限 30 有距离 b3 并发随流量起伏 · b2 5xx 长期贴地 事故路 · 池打满(贴 100%) 借不到连接 → 请求排队 → b3 并发高位 → b2 5xx > 1 req/s 动作:先看 b5/b6 哪台机器打满,再去查慢查询 biz 服务本体 · siliang-biz(独立于 backend 的 gunicorn 多 worker 业务后端) APP_WORKERS 默认 4 → instance = 机器-biz-序号(253-biz-0…3)· 容器内存限额 3G · 抓取间隔 30s 每 worker 各持一份 DB 池 30 → 单机 4 worker 合计至多 120 条 · Redis 上限 50 与 Agent 服务共享 这 11 张图全是 per-worker 视角:一条线异常就能定位到具体 worker(如 253-biz-2) 图例:emerald=HTTP/健康路 · violet=池与内存/GC · rose=事故路 · 虚线箭头=同款镜像 · 一切口径以主图谱 biz 盘「先读我」卡(b0)为准

💡 一句话理解

biz 是「另一家连锁分店」:独立于 backend 的 gunicorn 多 worker 业务后端(APP_WORKERS 默认 4)。这张盘不发明任何新指标——就是把核心盘已经教过的体检项目(HTTP 四件套、连接池两对卡、RSS/GC)在 biz 身上原样再量一遍,项目相同、读法相同,只有「读数」因服务而异。

所以学这课只需要记一张映射表:b1↔c16、b2↔c17、b3↔c18、b4↔c19、b5/b6↔m115/m116、b7/b8↔m117/m118、b9↔m101、b10↔m126;再加 biz 特有的三件事:handler 不含 /api/v2 前缀、Redis 与 Agent 服务共享(对方挤占)、池面板按机器 repeat 成对出现。

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

连锁体检的故事。你已经在前面的课程里学会了整套体检流程:量血压(HTTP 四件套:QPS、5xx、并发、延迟)、量血液粘度(连接池:借出 ÷ 藏书)、量体温(RSS 形态)、看白细胞(GC 速率)。现在公司新开了一家分店——biz 服务,设备与流程和总店(backend)完全一样,只是换了店名。体检报告自然也长一个样:项目一样、正常范围的判读方法一样,你不需要重新学,只需要对号入座。

这家分店的门牌号。biz 用 gunicorn 开了 4 家门店(APP_WORKERS 默认 4,每家是独立进程、独立内存、独立连接池)。Prometheus 每 30 秒来抄一次表,每家门店单独一本账:指标身份证上的 instance 写着「机器-biz-序号」,比如 253-biz-0 就是 253 这台机器上 biz 的 0 号 worker。菜单上的菜名(handler)是子路由模板,比如 /me——不含 /api/v2 前缀,去日志里搜接口名时别把前缀带上。

一处特别的合租。biz 的 Redis 图书馆不是独享的:它与 Agent 服务共享同一个 Redis(连接上限 max_connections=50)。合租意味着室友用得多,也会挤占你的池子——所以 b7/b8 的使用率莫名走高时,除了查 biz 自己,还要想想室友干了什么。另外这张盘的池面板按机器 repeat 成对出现(每台机器一对图),下钻时用仪表盘顶部的 HOST 变量按机器前缀切换。

类比里的东西系统里对应的东西
连锁店同一套体检流程biz 盘与核心盘同构的 11 张图(映射表 b1↔c16 … b10↔m126),读法完全复用。
分店店名job="siliang-biz"——biz 面板的指标全挂在这个 job 下,手工查询记得带上。
门店编号(253-biz-0)instance = 机器-biz-序号。图例里每条线就是一个 worker,一条线异常即可定位到具体 worker。
菜单短名(/me)handler 子路由模板,不含 /api/v2 前缀——图例里的接口名比完整路径短。
每家门店自己的藏书柜per-worker DB 池:10 + overflow 20 = 30;单机 4 worker 合计至多 120 条连接。
与邻居合租的图书馆Redis 池与 Agent 服务共享,上限 max_connections=50——室友挤占也会推高你的使用率。

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

  1. 打开 siliang-biz 仪表盘——确认顶部 job 是 siliang-biz、刷新 30s;对比核心盘的 15s,biz 的节奏整体慢半拍,属正常配置差异。
  2. 在 b1 挑一个 handler(如 /me)——再到核心盘 c16 找它;找不到是正常的:biz 的接口与 backend 不同,但图的读法一模一样。
  3. 看 b6 与 b8 的水平参考线——b6 应是 30(每 worker 的 DB 池上限 10+overflow 20),b8 应是 50(Redis max_connections,与 Agent 服务共享)。
  4. 展开 b5 的图例并切换 HOST 变量——确认池面板按机器 repeat 成对出现;用 HOST 变量按机器前缀下钻,看「哪台机器」的池更紧张。

🧠 必知必会 看懂本课全部面板的地基

gunicorn 多 worker
biz 用 gunicorn 起了 4 个 worker(APP_WORKERS 默认 4),每个是独立进程:独立内存、独立连接池、独立 GC。所以每张图都是 per-worker 视角,一条线异常就能定位到具体分店。
# instance 命名:<机器>-biz-<序号>
siliang_biz_http_requests_total{instance="253-biz-0"}
# → 单独看 0 号 worker 的请求计数
job=siliang-biz
biz 面板的指标全挂在 job="siliang-biz" 下。biz 是 per-worker 精确抓取、无漏计(口径卡 b0);自己写查询时带上 job 过滤,避免混入其他服务的系列。
# 手工查询的标准姿势:
sum(rate(siliang_biz_http_requests_total{job="siliang-biz"}[5m]))
handler 不含 /api/v2
图例里的接口名是子路由模板(如 /me),不含 /api/v2 前缀。按 handler 去日志里搜的时候,别把前缀带上,否则一行都搜不到。
# 图例显示 /me ↔ 完整路径是 /api/v2/me
sum(rate(siliang_biz_http_requests_total[5m])) by (handler)
DB 池 10+20=30
biz 的 DB 图书馆:平时最多借 10 条,高峰可临时加座(overflow)到 30——这是每个 worker 各一份,单机 4 worker 合计至多 120 条。使用率 80% 黄 / 95% 红。
# 使用率 = 借出 ÷ 藏书(b5 的口径)
max_over_time(siliang_biz_db_pool_connections{state="checked_out"}[1m])
  / max_over_time(siliang_biz_db_pool_capacity[1m])
Redis 与 Agent 共享
biz 的 Redis 池上限 max_connections=50,且这个 Redis 与 Agent 服务共享——对方用得多也会挤占你的额度。b7 使用率走高,先想室友再想自己。
# Redis 借出数(b8 绝对值口径,上限 50 参考线)
siliang_biz_redis_pool_connections{state="in_use"}
池面板成对出现
biz 部署在两台机器上,池面板按机器 repeat 成对出现:每台机器一对图。别把「只看到一张」误判为另一台没数据——用 HOST 变量切换机器即可。
# 按机器前缀下钻(HOST 变量的底层逻辑)
sum by (instance) (siliang_biz_db_pool_connections{state="checked_out"})
RSS 三形态
锯齿(涨跌交替)=健康;冲高后平台期=allocator 不归还(Python 常态,不是泄漏);线性爬升永不回落=真泄漏。判「真累积」用 deriv 算斜率。
# 每小时涨多少字节(口径卡告警:deriv > 5MB/h)
deriv(siliang_biz_process_resident_memory_bytes[1h])
GC 互证
gen0/1 周转快属正常;gen2 长期停滞 + gen0/1 活跃 = 有大对象赖在老年代不走——与 b9 的 RSS 平台期/爬升互证,才好定罪。
# 垃圾车出车频率(b10 口径,按代拆)
sum by (generation) (rate(siliang_biz_python_gc_collections_total[5m]))

📋 逐面板精讲 11 张图一张不落(顺序 = 主图谱 biz 盘顺序)

📖 口径说明(先读我)

它是什么:本盘使用说明书。三件事必须先背下来:生产是 gunicorn 4 worker(APP_WORKERS 默认 4);instance 名 = 机器-biz-序号(如 253-biz-0);handler 是子路由模板(如 /me,不含 /api/v2)。

回答什么问题:这张盘的「身份证规则」是什么?手工查询、日志检索、告警配置都从它对齐口径。

✅ 全组按这个口径读图:per-worker 一条线、接口名用短模板、机器用 HOST 变量切。

🚨 口径没对齐的典型事故:日志里搜 /api/v2/me 搜不到;把单机合计 120(30×4)当成单 worker 上限,判定阈值错 4 倍。

# 附 4 条建议告警(口径卡原文):
# 1. DB 池打满   2. Redis 池打满
# 3. 5xx 速率 > 1   4. deriv(RSS[1h]) > 5MB/h

联动:主图谱 b0 口径说明 ↗ · 相关课程:job / instance / label

🟢 HTTP QPS(按 handler)

它是什么:biz 每个接口每秒被叫几次。Counter 按 per-worker 精确抓取、无漏计,按 handler(子路由模板)拆开——这是 biz 的「客流表」。

回答什么问题:谁是最忙接口?流量结构有没有突变?调用方还活着吗?

✅ 曲线随业务节奏平滑起伏,各接口比例稳定(对照核心盘 c16 的读法)。

🚨 无缘无故突增 = 被刷/爬虫(去按 handler 定位并限流);某接口流量归零 = 上游调用方挂了(先问调用方再查自己)。

# 面板口径:每秒请求数按 handler 拆
sum(rate(siliang_biz_http_requests_total[5m])) by (handler)

联动:主图谱 b1 ↗ · 相关课程:rate() · HTTP 健康四件套(c16)

🟢 HTTP 5xx 错误率

它是什么:biz 每秒「服务员自己把菜搞砸」几次,按 handler 拆开。只盯 5xx(服务端的错),4xx 是客人的错不算在内。

回答什么问题:现在有没有事故?哪个接口在出事故?

✅ 长期贴地(0 附近小幅波动)。

🚨 > 1 req/s 就该排查——biz 的 5xx 多半源自 DB / Redis / 依赖故障,先看 b5/b7 池面板,再按 handler 直奔日志。

# 面板口径:只数 5xx(正则 status=~"5..")
sum(rate(siliang_biz_http_requests_total{status=~"5.."}[5m])) by (handler)

联动:主图谱 b2 ↗ · 相关课程:5xx vs 4xx · HTTP 健康(c17)

🟢 HTTP 当前并发请求数

它是什么:此刻 biz 店里多少客人没走(在途请求),按 method 拆。它是铁三角里的「并发」:并发 ≈ QPS × 延迟。

回答什么问题:请求在不在排队?变慢是流量变大还是处理变慢?

✅ 随 QPS 同步起伏,流量落下去它也跟着落。

🚨 持续高位 = 请求在排队——多半是 DB 慢查询或依赖抖动的旁证,配合 b5/b7 池面板一起看。

# 面板口径:在途请求数按 method 拆
sum(siliang_biz_http_requests_in_progress) by (method)

联动:主图谱 b3 ↗ · 相关课程:QPS·并发·延迟铁三角 · HTTP 健康(c18)

🟢 HTTP 延迟 P95(Top 5 慢接口)

它是什么:biz 最慢的 5 个接口排行榜(P95,单位秒)。要 P99 就进查询把 0.95 改成 0.99——面板默认比核心盘 top10 更聚焦,就是 biz 的性能优化清单。

回答什么问题:该先优化哪个接口?优化之后真的变快了吗?

✅ 榜单相对稳定,优化后对应接口的名次/数值下降。

🚨 新接口突然登顶且持续走高等于「最慢的 5% 用户在等它」——先查它是不是在等 DB/Redis(对 b5/b7 互证)。

# 面板口径:从延迟直方图水桶插值算 P95,topk 只留前 5
topk(5, histogram_quantile(0.95,
  sum(rate(siliang_biz_http_request_duration_seconds_bucket[5m])) by (le, handler)))

联动:主图谱 b4 ↗ · 相关课程:P50/P95/P99 · 直方图水桶与 topk

🟢 数据库连接池使用率(worker)

它是什么:biz 的 DB 图书馆借书证使用率 = 借出 ÷ 藏书(pool 10 + overflow 20 = 30,每 worker 一份)。80% 黄 / 95% 红。

回答什么问题:数据库连接是不是快借光了?排队是不是池引起的?

✅ 有涨有落的锯齿,高峰冲一把又回落。

🚨 贴 100% = 池打满、请求开始排队;哪台机器打满看 repeat 出来的两张图(HOST 变量切机器),再去查慢查询。

# 面板口径:使用率(分子 checked_out,分母 capacity)
max_over_time(siliang_biz_db_pool_connections{state="checked_out"}[1m])
  / max_over_time(siliang_biz_db_pool_capacity[1m])

联动:主图谱 b5 ↗ · 相关课程:连接池:图书馆借书 · 连接池台账(m115/m116)

🟢 数据库连接池借出数与上限

它是什么:上一张的绝对值版——各 worker 实际借了几条 + 上限 30 的水平参考线。百分比告诉你「比例」,绝对值告诉你「离天花板还剩几条」。

回答什么问题:现在到底借出去多少条?还剩多少余量给高峰?

✅ 借出线在参考线下方呼吸起伏,回落干脆。

🚨 借出线贴住 30 的参考线不下来 = 池打满实锤(和 b5 的 100% 互证),马上找占着连接不还的慢查询。

# 面板口径:借出数 + 上限参考线(max 取容量)
siliang_biz_db_pool_connections{state="checked_out"}
  + max(siliang_biz_db_pool_capacity)

联动:主图谱 b6 ↗ · 相关课程:连接池:图书馆借书 · 连接池台账(m116)

🟢 Redis 连接池使用率(worker)

它是什么:biz 的 Redis 借书证使用率。特别注意:这个 Redis 与 Agent 服务共享——对方用得多也会挤占你的池子,这是一张「合租的水电表」。

回答什么问题:Redis 连接是不是成了瓶颈?紧张是我们自己还是室友造成的?

✅ 低水位平稳波动,随业务节奏小幅呼吸。

🚨 使用率持续走高:biz 没发版也要先想室友(Agent 服务)与共享上限 50;确认时段相关性后再下钻归因。

# 面板口径:Redis 使用率(in_use ÷ capacity)
max_over_time(siliang_biz_redis_pool_connections{state="in_use"}[1m])
  / max_over_time(siliang_biz_redis_pool_capacity[1m])

联动:主图谱 b7 ↗ · 相关课程:连接池:图书馆借书 · 连接池台账(m117/m118)

🟢 Redis 连接池借出数与上限

它是什么:Redis 池的绝对值版——各 worker 借出几条 + max_connections=50 参考线。和 b6 一样:使用率看趋势,绝对值看「离天花板多远」。

回答什么问题:全机一共借出去几条 Redis 连接?50 的上限还剩多少余量?

✅ 借出线远低于 50,且能随流量回落。

🚨 贴近 50 = 合租的图书馆快没书可借(连 biz 带 Agent 一起算)——连接开始等待,接口延迟跟着抖。

# 面板口径:借出数 + 上限参考线
siliang_biz_redis_pool_connections{state="in_use"}
  + max(siliang_biz_redis_pool_capacity)

联动:主图谱 b8 ↗ · 相关课程:连接池:图书馆借书 · 连接池台账

🟢 进程内存水位(RSS/VMS)(按容器拆分)

它是什么:biz 每个 worker 的体温计。每台机器一张图、每 worker 一条 RSS 线(1 分钟窗口最大值抹抓取抖动);VMS 是虚拟地址空间,虚高只做参考。注意它只反映被 Prometheus 抓到的 worker。

回答什么问题:biz 的内存健康吗?是正常呼吸还是真在偷偷涨?

✅ 锯齿 = 健康;冲高后平台期 = allocator 不归还(Python 常态,内存留着复用,重启才还)。

🚨 线性爬升永不回落 = 真累积(泄漏)——用 deriv 量化斜率,配合 b10 的 GC 面板互证,再对照流量(b3)排除「单纯业务变忙」。

# 面板口径:1 分钟窗口最大值,每 worker 一条线
max_over_time(siliang_biz_process_resident_memory_bytes[1m])
# 判「真累积」:deriv(...[1h]) 每小时涨多少字节(> 5MB/h 是口径卡告警线)

联动:主图谱 b9 ↗ · 相关课程:泄漏判定心法 · RSS / VMS / working_set · 内存全局水位(m101)

🟢 GC 各代回收速率

它是什么:biz 的垃圾车出车频率(按代拆)。gen0/1 新生代周转快、出车勤属正常;gen2 老年代出车少也正常,但「长期不出车」就有问题了。

回答什么问题:内存涨是「活多垃圾多(正常)」还是「大对象赖着不走(驻留)」?

✅ gen0/1 频繁出车、gen2 偶尔出车,整体节奏稳定。

🚨 gen2 长期停滞而 gen0/1 活跃 = 有大对象驻留——与 b9 的 RSS 平台期/爬升互证后,按泄漏思路下钻。

# 面板口径:各代回收速率
sum by (generation) (rate(siliang_biz_python_gc_collections_total[5m]))

联动:主图谱 b10 ↗ · 相关课程:Python GC 分代回收 · 进程资源四件套(m126)

🏭 生产实战 real world

场景 1 · biz 接口报 500:从 b2 到根因的五分钟

用户反馈业务接口报错。biz 的 5xx 多半不是代码炸了,而是 DB / Redis / 依赖故障连坐——顺着「错误率 → 池 → 日志」的链走。

# 第 1 步:哪个接口在出 500?(b2)
sum(rate(siliang_biz_http_requests_total{job="siliang-biz", status=~"5.."}[5m])) by (handler)
  # > 1 req/s 即进入排查;锁定的 handler 去日志搜(记得去掉 /api/v2 前缀)

# 第 2 步:是不是 DB 池打满连坐?(b5/b6)
max_over_time(siliang_biz_db_pool_connections{state="checked_out"}[1m])
  / max_over_time(siliang_biz_db_pool_capacity[1m])
  # 贴 1.0(100%)= 请求在等连接 → 去找占着连接的慢查询

# 第 3 步:是不是 Redis 挤占?(b7,注意与 Agent 服务共享上限 50)
siliang_biz_redis_pool_connections{state="in_use"}
  # 贴近 50 = 合租池紧张 → 看 b8 的机器分布,确认是 biz 还是室友

判读:池使用率回落后 5xx 跟着归零 = 池连坐实锤;池没满但 5xx 不断 = 去依赖(DB/Redis 本身)查故障。

场景 2 · 接口变慢:b4 榜单 + 池 + 并发三角互证

「最近有点慢」的模糊反馈。先让 b4 榜单告诉你该怀疑谁,再用 b3/b5 区分「排队慢」还是「处理慢」。

# 1) 最慢的 5 个接口(b4;想看最惨的 1% 就把 0.95 改 0.99)
topk(5, histogram_quantile(0.95,
  sum(rate(siliang_biz_http_request_duration_seconds_bucket[5m])) by (le, handler)))

# 2) 并发是否高位不落(b3)——持续高位 = 在排队
sum(siliang_biz_http_requests_in_progress) by (method)

# 3) 按 worker 拆池借出数(b6 变体)——单 worker 打满会被求和掩盖
sum by (instance) (siliang_biz_db_pool_connections{state="checked_out"})
  # 某个 instance 独自顶到 30 = 那家「分店」的池被打满

判读:榜单头部接口 + 并发高位 + 池贴顶三者同时出现 = 慢查询占连接的连锁反应;只有榜单高、池空闲 = 接口自身逻辑或外部调用慢。

场景 3 · 「biz 内存涨了」:b9 三形态定罪 + deriv 量化

有人盯着 b9 喊内存涨。先别慌——三种形态三种结论,用形态和斜率说话。

# 1) 看形态(b9):每 worker 一条线
max_over_time(siliang_biz_process_resident_memory_bytes[1m])
  # 锯齿/冲高回落 = 健康;冲高后横走 = 平台期(allocator 不归还,常态)
  # 斜着向上永不回落 = 真累积,进入第 2 步

# 2) 量化斜率:每小时涨多少字节
deriv(siliang_biz_process_resident_memory_bytes[1h])
  # 持续 > 5MB/h = 口径卡的「持续爬升」告警线(以 b0 口径卡为准)

# 3) GC 互证(b10):gen2 停滞而 gen0/1 活跃 = 大对象驻留
sum by (generation) (rate(siliang_biz_python_gc_collections_total[5m]))

# 4) 排除「单纯变忙」:流量同步涨了吗?
sum(siliang_biz_http_requests_in_progress) by (method)

判读:deriv 持续超线 + gen2 停滞 + 流量没涨 = 泄漏,按内存盘课程下钻;流量同涨 = 先按容量问题处理。

场景 4 · 给 biz 配齐 4 条告警(照抄口径卡 b0 的清单)

口径卡附了 4 条建议告警:DB 池打满 / Redis 池打满 / 5xx 速率 >1 / RSS 持续爬升。照着口径写进告警规则文件。

groups:
- name: siliang-biz # 口径卡 b0 的 4 条建议告警
  rules:
  - alert: BizDbPoolNearFull
    expr: max_over_time(siliang_biz_db_pool_connections{state="checked_out"}[1m]) / max_over_time(siliang_biz_db_pool_capacity[1m]) > 0.95
    for: 5m        # 95% 红:池打满的前最后窗口
  - alert: BizRedisPoolNearFull
    expr: max_over_time(siliang_biz_redis_pool_connections{state="in_use"}[1m]) / max_over_time(siliang_biz_redis_pool_capacity[1m]) > 0.95
    for: 5m        # 注意与 Agent 服务共享上限 50
  - alert: BizHttp5xxRate
    expr: sum(rate(siliang_biz_http_requests_total{status=~"5.."}[5m])) > 1
    for: 5m        # > 1 req/s:按 handler 直奔日志
  # 第 4 条:deriv(siliang_biz_process_resident_memory_bytes[1h]) > 5MB/h
  # 阈值口径以 b0 口径卡原文为准

判读:告警触发后,处理路径分别是「查慢查询 / 查室友 Agent / 按 handler 查日志 / 按泄漏链下钻」。

场景 5 · b1 流量突增:被刷识别与调用方失联

没有发版、没有活动,b1 上某个 handler 的线突然竖起来——要么被刷,要么某接口突然消失(调用方挂了)。两种都要按 handler 处理。

# 1) 突增定位:谁在涨?(b1,临时把窗口改小看细节)
sum(rate(siliang_biz_http_requests_total[1m])) by (handler)

# 2) 近 1h 该 handler 总量对照(increase 看累计)
sum by (handler) (increase(siliang_biz_http_requests_total[1h]))

# 3) 谁消失了:对比各 handler 速率(归零的 = 调用方挂了)
sum(rate(siliang_biz_http_requests_total[5m])) by (handler)
  # 无发布无活动却突增 = 被刷/爬虫:按 handler(短模板名)去限流/封禁

判读:突增的 handler 若伴随 5xx(b2)与池紧张(b5),先限流止血再查来源;归零的接口则先呼调用方。

⚠️ 常见坑 pitfalls

坑 1 · 拿图例里的 handler 去日志搜全路径 — 症状:搜 /api/v2/me 一行结果都没有,以为日志丢了。原因:biz 面板的 handler 是子路由模板,不含 /api/v2 前缀。正解:按短模板名(/me)去搜,或检索时把前缀拼回去。
# 错: # grep "/api/v2/me" → 图例名 ≠ 完整路径,搜不到
# 对: # grep " /me " → handler 是子路由模板(口径卡 b0)
坑 2 · 把单机合计 120 当单 worker 上限 — 症状:借出 35 条就报警「池满了」。原因:DB 池 30 是每 worker 一份(10+overflow 20),单机 4 worker 合计至多 120;看单 worker 才对。正解:按 instance 拆开看,b6 的参考线就是 30。
# 错: sum(siliang_biz_db_pool_connections{state="checked_out"})          # → 4 worker 加总,掩盖单打满
# 对: sum by (instance) (siliang_biz_db_pool_connections{state="checked_out"})
坑 3 · b7 走高只翻 biz 代码 — 症状:Redis 池使用率高,查 biz 一晚上毫无头绪。原因:这个 Redis 与 Agent 服务共享(上限 50),室友用得多也会挤占你的池子。正解:先看时段相关性——室友的繁忙时段是否吻合,再定是谁的锅。
# 错: # b7 高 → 只查 biz 代码(合租水电表,只查一户没用)
# 对: # b7 高 → 先想共享(Agent 服务),再对照时段归因
坑 4 · 对 RSS 求 rate — 症状:内存曲线画出无意义的锯齿噪声。原因:siliang_biz_process_resident_memory_bytes 是 Gauge(温度计),Gauge 求 rate 无意义。正解:看形态直接读数;判「真累积」用 deriv 算斜率。
# 错: rate(siliang_biz_process_resident_memory_bytes[5m])   # → Gauge 求 rate
# 对: deriv(siliang_biz_process_resident_memory_bytes[1h])  # → 每小时涨多少字节
坑 5 · 见平台期就喊泄漏 — 症状:RSS 冲高后横着走,连夜回滚。原因:冲高后平台期 = allocator 不归还(Python 常态:内存留着复用,重启才还),不是泄漏。正解:线性爬升永不回落才是真累积;平台期盯住 deriv 不超线即可。
# 错: # 平台期 → 判泄漏 → 回滚(白折腾)
# 对: # deriv(...[1h]) 持续 > 5MB/h 才按泄漏链下钻
坑 6 · 只看百分比卡或只看绝对值卡 — 症状:只看 b5 说「才 60%,没事」,结果绝对值已贴顶(容量被改小过);或只看 b6 说「才借 20 条」,不知道上限已变。原因:两张卡回答不同问题。正解:b5 看趋势与告警,b6 看离天花板多远,成对读。
# 错: # 只读一张卡就下结论
# 对: # b5(使用率)+ b6(借出 vs 上限 30)成对看
坑 7 · 以为池面板少了一台机器的数据 — 症状:找不到「另一台机器」的池图,报工单说数据丢了。原因:池面板按机器 repeat 成对出现,另一张在旁边/下一行。正解:用 HOST 变量按机器前缀下钻,确认两台都在。
# 错: # 只见一张 → 报「数据丢失」
# 对: # repeat 成对出现;HOST 变量切机器前缀查看
坑 8 · 忘了 biz 与 core 是两个 job — 症状:手工查询把 siliang_backend_worker 的指标当成 biz 的,数值对不上面板。原因:biz 全部指标挂在 job="siliang-biz" 下,两套服务不共享指标。正解:查 biz 永远带 job="siliang-biz" 过滤(镜像的是「图」,不是「数据」)。
# 错: sum(rate(siliang_http_requests_total[5m]))        # → 这是 backend 的指标
# 对: sum(rate(siliang_biz_http_requests_total[5m]))     # → biz 的指标带 biz_ 前缀

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

Q1 · 用一句话向新人解释「biz 盘是核心盘的精简镜像」,并说出映射表里任意三对。

参考答案:biz 盘不发明新东西,就是把核心盘教过的体检项目(HTTP 四件套、连接池两对卡、RSS/GC)在 biz 服务上原样再量一遍,读法完全复用。映射表任意三对:b1↔c16(QPS)、b5/b6↔m115/m116(DB 池两张卡)、b9↔m101(RSS 形态)、b10↔m126(GC 速率)等。

Q2 · b7 的 Redis 使用率冲到 80%+,但 biz 近一周没发版,可能的原因是什么?

参考答案:先想「合租」——这个 Redis 与 Agent 服务共享(上限 max_connections=50),室友用得多也会挤占你的池子,对照时段相关性确认是不是 Agent 的繁忙时段。再查 biz 自己:流量是否突增(b1)、是否有新接口在狂用 Redis。共享池是全机额度,谁的锅要看谁的时间线吻合。

Q3 · b9 的 RSS「线性爬升」和「冲高后平台期」分别怎么判、怎么办?

参考答案:线性爬升永不回落 = 真累积(泄漏):用 deriv(RSS[1h]) 量化,持续大于 5MB/h(口径卡告警线)即立案,配合 b10 看 gen2 是否停滞、按内存盘课程下钻找凶手。冲高后平台期 = allocator 不归还,是 Python 的常态(内存留着复用,重启才还),不是泄漏——盯住 deriv 不超线即可,不用半夜回滚。

Q4 · 口径卡 b0 附的 4 条建议告警是哪 4 条?各自触发后第一动作是什么?

参考答案:① DB 池打满——查占着连接不还的慢查询;② Redis 池打满——先想与 Agent 服务共享(对方挤占),再查自己;③ 5xx 速率 > 1 req/s——按 handler(短模板名)直奔日志,顺 b5/b7 看是不是池连坐;④ deriv(RSS[1h]) > 5MB/h——按泄漏链下钻(形态 → GC 互证 → 流量对照)。

← 上一课:🎬 视频后台流水线:结算工厂 📚 课程目录 下一课:🟡 容器层:盒子视角 →