已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
背景:我负责一个订单中台服务,Spring Boot 3.2 + JDK 17(G1,-Xmx8g)+ MySQL 8,K8s 1.28.9 / cgroup v2,Pod 规格 8C16G,4 副本,在线接口峰值 QPS 约 600,P99 基线 80ms 左右。服务里还内嵌了一个 Quartz 调度器,每天凌晨 02:00 和 10:30 各跑一次「订单对账 + 报表聚合」的批任务,单次约 8~15 分钟,主要是大表扫描 + 批量写 MySQL + 少量 CPU 聚合。
现象:两次批任务运行窗口内,同一批 Pod 的在线接口 P99 从 80ms 稳定飙到 320~480ms,持续整个批任务时长,批任务结束后立刻回落。P50 基本不动,错误率没有明显上升——典型的「慢但没挂」。凌晨那次值班同学一度怀疑是数据库慢查询。
已尝试的排查与结果:
ThreadPoolExecutor.getTask() 和 synchronized 上——怀疑是批任务和在线请求共用了 Tomcat 工作线程池之外的公共线程池(我们历史代码里有个 @Async 默认走 commonPool 的埋点上报,批任务里也大量调它);@Async 改成显式独立线程池后复跑,P99 峰值从 420ms 降到 150ms——确认方向没错,但只降了一半。卡点:现在剩下的 150ms 抖动我定位不动了,怀疑是 GC 压力(批任务的对象分配把 G1 的 mixed GC 频率拉高)+ 公共池竞争叠加。
期望得到的答案形态:
ForkJoinPool.commonPool 被批任务吃满、反过来拖死在线埋点的坑?怎么防?欢迎带真实案例和参数来,越具体越好。