应结合nginx日志中$request_time与$upstream_response_time的差值定位瓶颈:差值>300ms在nginx层;两者接近且偏高在java层;$upstream_response_time为“-”且状态码502/504则java服务未响应。

Java 应用通过 Nginx 暴露服务时,性能瓶颈可能出现在 Nginx 层、网络链路、或 Java 服务本身。要准确定位 Java 部署的运行瓶颈,不能只盯着 Java 进程,而需结合 Nginx 日志、系统指标与 Java 侧工具做分层交叉验证。
看 Nginx 日志中的时间差定位瓶颈归属
在 Nginx access log 中必须记录两个关键字段:$request_time(客户端全程耗时)和 $upstream_response_time(后端 Java 返回响应的时间)。二者对比可快速划清责任边界:
- 若 $request_time 明显大于 $upstream_response_time(例如差值 >300ms),说明延迟发生在 Nginx 本体:可能是 SSL 握手慢、大请求体解析耗时、gzip 压缩、proxy_buffering 缓冲等待,或客户端弱网上传
- 若 两者数值接近且同时偏高(如都稳定在 1.2s 左右),问题大概率在 Java 应用内部:数据库慢查、线程池饱和、GC 暂停、远程调用阻塞等
- 若 $upstream_response_time 为 “-” 或极小值(如 0.000),但状态码是 502/504,说明 Nginx 根本没连上 Java 服务——检查端口通断、连接池耗尽、或 Java 进程已僵死
进容器/服务器查 Java 进程真实负载
绕过 Nginx 直接 curl 后端 Java 接口(如 curl -w "@format.txt" http://localhost:8080/api/test),用 -w 输出详细时间字段(TTFB、total_time),确认是否仍慢。若直连也慢,再深入 Java 层:
- 用 jstat -gc
查 GC 频率与停顿:如果 YGCT/FGCT 高或 GCT 占比超 10%,说明 GC 是瓶颈 - 用 jstack
抓线程快照,搜索 WAITING或BLOCKED线程数是否异常多;重点关注http-nio或ForkJoinPool线程状态 - 用 async-profiler 采样 CPU 火焰图:
./profiler.sh -e cpu -d 30 -f profile.html <pid></pid>,直观看到哪些方法吃掉最多 CPU
结合 Nginx 和 Java 的监控指标交叉印证
单看某一层容易误判。比如 Nginx 的 Active connections 长期高位 + Java 线程池 activeCount 接近 max,说明请求在排队;而如果 Nginx Waiting 数高但 Java 线程空闲,则问题出在 Nginx 配置(如 worker_connections 不足或 upstream keepalive 过小):
- 启用 Nginx
stub_status,定时采集Accepts/Handled/Requests和连接状态 - 在 Java 应用中暴露 Micrometer 指标(如
tomcat.threads.busy、jvm.gc.pause),接入 Prometheus+Grafana - 对同一请求打标记(如加
X-Request-ID),在 Nginx 日志、Java 应用日志、APM 调用链中追踪完整生命周期
特别注意容器环境下的隐性限制
Java 应用跑在 Kubernetes 或 Docker 中时,cgroup 限制造成的“静默降频”常被忽略:
- 进 Java 容器执行
cat /sys/fs/cgroup/cpu/cpu.stat | grep throttled,若throttled_time > 0,说明 CPU 被强制节流,即使top显示 CPU 使用率不高 - 检查 JVM 是否识别到容器内存限制:Java 8u191+/10+ 默认支持
-XX:+UseContainerSupport,否则可能因 Xmx 设得过大触发 OOMKilled - 确认
worker_processes没设为auto,应设为容器实际分配的 CPU 数(如 limits.cpu=1 → worker_processes 1)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











