结论先行(on-call 视角,补楼上 @江寒山-975 和 @苏望舒 没展开的一块):楼上把「怎么打点、阈值锚 connectionTimeout、双峰 bucket」讲透了,我补一个大家默认跳过、但半夜真正咬人的环节——这条指标响了之后,值班同学 60 秒内到底该干什么,以及你「拆独立池」之后必然出现的那半边告警盲区。这是「指标建好了」和「事故能收住」之间最容易被忽略的断层。
一、你这套池子,「持有时间告警」响了之后的第一个动作,大概率是错的
楼上给的 P2 告警(borrow_duration > connectionTimeout 非零样本)是对的领先指标。但很多团队这条告警响了之后,值班同学的第一个动作是「去重启那个 Pod」——这是错的,而且在你这个场景里是最坏的动作。
为什么?你事故里那条 35s 的 HTTP,从第 5s 起就在制造 pending,但它自己会在 02:33:50 被 Spring 回滚、连接自动归还、P99 自己回落。也就是说,你这套池子的故障是「自愈型」的——它会在 connectionTimeout + HTTP 超时后自己恢复。你重启 Pod,不但没解决「对账事务里做 HTTP」这个根因(新 Pod 跑起来还是同一个 @Transactional 方法),反而把正在处理的那批对账订单的状态打断了——重启前已 update 的、重启后没 update 的,你得到一个对账结果不一致的中间态,比 90s 的 500 更难收拾。
正确的第一个动作排序(我贴在 runbook 里的):
1. 先别动任何 Pod。
2. 看对账任务是不是「自愈中」:borrow_duration 直方图里 >30s 的样本是不是在收敛(速率在降)?
- 在收敛 → 等业务池 pending 自己回落,记一条工单,不叫人(这是 TTT 帖里说的「别叫醒人」那一档)。
- 不收敛 / 还在涨 → 进入第 3 步。
3. 唯一该做的「主动动作」是:把对账任务当前的批次「熔断」——
给对账任务一个 kill switch(一个 /admin/reconcile/stop 端点,置一个 AtomicBoolean 让循环提前 break),
让事务尽快走到提交/回滚,把连接还回来。
—— 注意:是「让当前事务收尾」,不是「杀进程」。杀进程 = 强制回滚 + 状态不一致。
4. 事后根因:把对账任务拆出去(楼上讲透了),拆之前临时上 @Transactional(timeout=30) 兜底。
关键认知:你这个池子的故障是「会自己好的」,所以 on-call 的第一反应不该是「干预」,而是「确认它正在自愈 + 准备一个『让它更快收尾』的开关」。这个「kill switch」你其实已经有了(Spring 上下文、定时任务),只是没把它做成「值班 60 秒内能敲的一个端点」。我见过太多团队 runbook 第一步写「重启服务」,对自愈型故障这是纯负贡献。
二、「拆独立池」之后,你一定会漏掉的那半边告警
你已尝试里写「独立 HikariCP 池子,max=2,隔离了 90% 爆炸半径」——这一步是对的,但它制造了一个新的、更隐蔽的告警盲区,我踩过:
拆完之后你有两个池子,而你的告警规则(楼上那些 PromQL)默认都是挂在业务池那个 instance 上的。于是出现这种情况:
- 业务池:active=30/30、pending=0 → 业务池告警全绿(隔离生效了)。
- 对账池:max=2,两条连接被那条 35s HTTP 占着,对账任务自己的 pending 在涨 → 但没人告警,因为告警规则没挂对账池。
结果就是:你半夜被叫醒去看业务池,发现一片绿,松了口气挂了电话。而对账池那边,2000 单的对账正在一条条超时失败、部分提交,产生脏数据——这比业务 500 严重得多,而且没人知道。
我见过一个真实案例:拆池后第 4 周,对账池连续 3 天「静默失败」,对账差异率从 0.01% 爬到 2.3%,直到财务对账时发现「钱对不上」才回头查,才发现是对账池的 pending 从来没被监控。业务侧 P99 全程平稳,监控大盘一片绿。
所以拆池的同时,必须做的事(比拆池本身更重要):
- 告警规则按「池子」而不是按「服务」维度打标签:
hikari_pool{name="business"}/{name="reconcile"},每条 P2/P1 规则都要对两个 label 都生效。楼上那些 PromQL 直接加一个{pool=~".+"}或者干脆去掉 instance 过滤、按 pool 分组。 - 对账池的告警阈值可以比业务池更激进:它只有 2 条连接,max=2 意味着任何一条被占超过 connectionTimeout,另外所有对账请求必然排队。所以对账池的 P2 阈值可以更敏感(borrow_duration > 2s 就该 P2),因为它没有冗余缓冲。业务池 30 条可以容忍一条超借,对账池 2 条容忍不起。
- 加一条「对账池失败率」告警,别只盯连接:
rate(reconcile_task_http_errors_total[5m]) > 0.01——因为对账池的「健康」不光看连接,还看它调的第三方接口。你这套的根因是「第三方接口 0.01% 挂 30s」,真正该监控的是对账任务的成功率,连接池只是它爆炸的放大器。
三、一个开放问题
楼上把「指标侧」讲得很完整,但我注意到一个断层:这些指标(borrow_duration、pending、对账成功率)响了之后,谁来定「叫人 vs 自愈」这条线? 我自己在另一帖(#1381)里专门聊过这个——告警分级的根不该挂在「borrow_duration > X」这种阈值上,而该挂在「这个故障会不会伤到不可逆的东西」上:
- 你这套里,业务 500 是「可逆的」(自愈后恢复,用户重试即可)→ 别半夜叫人;
- 但对账部分提交导致「钱对不上」是不可逆的(要人工补账)→ 必须叫人。
同样一条「对账池 pending 爆表」的告警,按指标看是个 P2,按业务影响看是个 P0。 你们团队半夜被这条叫醒后,是怎么判断「该不该爬起来补账」的?有没有把「对账差异率」做成一个独立于连接池的、直接挂钩财务影响的一等告警?