已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
背景
我负责一个核心查询服务:Spring Boot 3.2 + JDK 17.0.10(G1,-Xmx4g -XX:MaxGCPauseMillis=50)+ MySQL 8 + Redis 7,K8s 1.28.4 / containerd 1.7 / cgroup v2 / 内核 5.15,单 Pod cpu.max=200000 100000(即 limit=2 核,burst 关闭),memory limit=6G。线上 QPS 约 600~800,P50 一直稳定在 12~18ms。
现象
container_cpu_usage_seconds_total 折算出的 CPU 使用率只到 limit 的 35~55%,远没到 100%,但读 cgroup v2 的 cpu.stat 能看到 nr_throttled 在涨、throttled_usec 非零,粗算 throttle 比例只有 2~5%;已尝试的排查与结果
-Xlog:gc*=file:gc.log:time,uptime,level,tags 全量日志,尖峰窗口内 0 次 Full GC,Young GC 平均 4ms、最大 9ms,Pause Young (Normal) 与 P99 尖峰时间戳没有重叠——基本排除 GC;jstat -print_compilation_time <pid> 1000 在尖峰窗口抓了 3 次,Compiled 计数没有明显跳变,Failed 恒为 0,看起来也不是去优化(deopt)风暴——但我不知道怎么看 C2 编译线程是否阻塞了业务线程;active 峰值 18/20、pending 恒为 0,connectionTimeoutCount=0;MySQL 侧 performance_schema.events_statements_history 里 P99 RT 与平时一致,排除 DB;jstack 在尖峰窗口抓了 5 次(每 200ms 一次),看到一部分 Tomcat 工作线程停在 at java.base/java.lang.Object.wait(...) + ThreadPoolExecutor.getTask,一部分停在 epoll_wait,没有看到明显的锁竞争 / 死锁,但也没看到明确的「被内核 throttle」的栈帧(我猜内核层 throttle 不会体现在 Java 栈里);@Scheduled(fixedRate=60s) 的指标上报 + 每 5 分钟的日志轮转,量级很小,理论上不该造成 400ms 毛刺;nr_throttled 也还是非零,说明不是「限流=0」才正常。期望得到的答案形态
wall 模式 + jstat -print_compilation_time + cgroup cpu.stat 三者时间戳对齐的方法,或者更直接的 perf / bpftrace 脚本);cpu.max.burst(cgroup v2)、还是把 CPU 密集逻辑(JSON 序列化、正则、加解密)挪到异步线程池?各自的代价是什么?throttled_usec / usage_usec 的阈值?还是 P99 直方图的分位数?)。参考:社区里 @苏清和 的《K8s 上 JVM 服务内存 limit 怎么定》和 @苏星河 的《K8s Java 17 容器被 OOMKill 但 JVM 日志显示堆未打满》都讲了 K8s + JVM 的坑,但都是内存侧;@程知远 的《Java 服务发布后前 3 分钟 P99 偏高》是 JIT 侧;这次我卡在 cgroup v2 CPU 限流的「小比例 throttle → 大 P99 抖动」 这个上游位置,想听一线经验。