系统上线的第一个小时,页面全是 502。
后端同学说:我本地 curl 是通的。运维说:Nginx 我一个字没改。
两边都没说谎。后端服务监听的是 127.0.0.1:8080,Nginx 转的是 10.0.1.12:8080。本机 curl 当然通,跨机当然不通。
这种“谁都对,系统就是不通”的场面,是应用运维每天的主旋律。
所以这 108 条按一条链路排:Nginx 进来 → Tomcat 接住 → JVM 跑起来 → 线上联调。你顺着这条链路查,就不会跳层。
* * *
每一次 502,拆开看都是一串“当时看着都对”的决定:Nginx 配了 60 秒超时、后端接口跑了 61 秒、连接池给了 20 个、线程池给了 500 个。
单看每一条都合理,凑在一起就是事故。
所以这篇的写法是:一条命令,配一个坑。
命令你抄走。坑你记住——真出事的时候,救你的是坑,不是命令。
全文 108 条,编号连续。搜“42”,就是第 42 条。
目 录
1Nginx 配置01–182Nginx 排障19–363Tomcat 部署37–544Tomcat 与线程55–725JVM 与内存73–906联调与线上91–108
* * *
Nginx 的配置难的不是语法,是顺序。同样的几行,放错层级、改个斜杠,行为就完全变了。
01检查配置语法
坑通过只代表语法对,逻辑错照样能启动,路径写歪也照样报 success
02看编译参数
坑装没装 ssl、stream 模块全在这行 configure arguments 里,没有就是没有
03平滑重载
坑reload 不断连接,但新配置里 upstream 指向不存在的地址,新 worker 会全挂
04看主进程与 worker
坑只剩 worker 没有 master,说明 master 崩过,reload 也不会生效
05server_name 匹配
坑多个 server 都没匹配上时走默认 server,默认是谁取决于配置顺序,不显式写就是坑
06location 匹配优先级
坑正则 location 会盖掉普通前缀,加了 ^~ 才不再往下查正则
07root 与 alias 的区别
坑alias 结尾不加斜杠路径少一层,root 会把 location 拼到路径后面,两者结果差一个目录
08单页应用刷新 404
坑漏了最后的 /index.html 兜底,前端路由一刷新就是 404
09proxy_pass 结尾斜杠
坑结尾有斜杠会截掉 location 前缀,没有会原样带上,少一层多一层全靠这个
10反代 header 四件套
坑少了 X-Forwarded-Proto,后端生成的回调地址会变回 http,登录直接失败
11多级代理取真实 IP
坑不配 real_ip,日志里全是负载均衡的 IP,出事查不到来源
12上传大小限制
坑在 http 里配了、location 里又覆盖了,最终生效的是最里面那层
13超时三件套
坑read_timeout 比接口耗时短,长任务接口必然 504,而且日志里看不出是超时
14开 gzip 压缩
坑对图片压缩纯浪费 CPU,text/html 默认就压,不用重复写
15静态资源缓存
坑把 index.html 也设了长缓存,发版后用户看到的还是旧页面
16upstream 负载均衡
坑默认轮询不做主动健康检查,一台挂了就是 50% 请求失败
17自定义日志格式
坑request_time 是整个请求耗时,upstream_response_time 才是后端耗时,分不清就定位错方向
18HTTP 跳 HTTPS
坑跳转写在 location 里,POST 会被转成 GET,参数全丢,用户看到的是“提交后回到首页”
Nginx 只是最后一环。它报的每一个错,都在替别人背锅。看懂 error.log 那一行,比查十个博客有用。
19502 先看错误日志
坑日志里必有 connect() failed 或 upstream prematurely closed,先看这一行再猜
20502 检查后端端口
坑后端在但只监听 127.0.0.1,Nginx 连 10.x 地址必然 502,本地 curl 却是通的
21502 后端超时被杀
坑调大 proxy_read_timeout 只是掩盖,后端慢才是根因
22504 到底谁慢
坑两个都大是后端慢,只有 request_time 大是客户端慢,方向完全不同
23413 上传被拒
坑改了 Nginx 还是 413,因为 Tomcat 的 maxPostSize 也在拦
24499 是什么意思
坑499 是客户端主动断开,多半是接口太慢用户关了页面,不是 Nginx 的错
25403 但文件存在
坑目录缺 x 权限,文件有读权限也访问不了,这是最容易漏的一环
26证书链不完整
坑只配站点证书不带中间证书,PC 上正常、手机上全红
27证书有没有过期
坑证书过期当天才发现,是因为监控里根本没这一项
28跨域预检被拦
坑add_header 在 location 里会覆盖外层的,写一处漏一处
29WebSocket 连上就断
坑少了 http_version 1.1,握手成功也会立刻断开
30限流把业务限死
坑burst 设 0,稍一突刺就 503,业务侧看到的是“随机失败”
31连接数不够
坑只调这个不调 worker_rlimit_nofile,句柄到顶照样报 too many open files
32找 TOP 访问 IP
坑日志格式改过之后字段位置全变,$1 不一定还是 IP
33找最慢的接口
坑字段号由 log_format 决定,抄别人的命令前先确认自己的格式
34临时摘掉一台后端
坑直接删掉 server 行,恢复时容易漏配参数
35确认 VIP 在谁身上
坑VIP 没漂,应用还连旧主,报的错看起来像网络问题
36静态文件 404 但确实在
坑SELinux 拦了 Nginx 读这个目录,日志里是 permission denied,看着像路径配错
Tomcat 的坑集中在三处:路径、端口、内存。三样对不上,日志里的报错都长得很像。
37看 Tomcat 版本
坑不报版本的求助基本没法答,不同大版本行为差异很大
38看关键目录
坑work 目录不清理,改了 JSP 不生效,看着像缓存问题
39部署 war 包
坑war 文件名就是访问路径,叫 app-1.0.war 就得访问 /app-1.0
40指定根路径部署
坑context 占了 /,其他应用全 404,而且很难想到是这里
41Connector 核心参数
坑maxThreads 调到 2000 但 CPU 只有 8 核,线程全在等锁,越调越慢
42配置线程池
坑配了 Executor 但 Connector 没写 executor 属性,配置完全不生效
43设置 JAVA_OPTS
坑写在 /etc/profile 里,systemd 启的服务读不到,要写 bin/setenv.sh
44看启动日志
坑catalina.out 不切割,三个月能到几十 G,磁盘就是这么满的
45查端口冲突
坑shutdown 端口和 http 端口是两回事,关不掉往往是 8005 被改过
46一机多实例
坑只改 http 端口,shutdown 和 ajp 撞了,第二个实例起不来
47部署后 404
坑没有 classes 目录,说明打包时编译产物没进去
48启动卡住
坑卡在 Deploying web application 后面,多半是连接池初始化在等超时
49日志切割
坑用 copytruncate 切 catalina.out,正在写的那段会丢
50关不掉进程
坑shutdown.sh 依赖 8005,端口不对永远关不掉,最后只能 -9,而 -9 会丢数据
51关掉热部署
坑生产上开着 reloadable,反复 reload 会把 Metaspace 撑爆
52静态资源交给 Nginx
坑Tomcat 送静态文件慢一个数量级,但加了 Nginx 缓存后发版不生效
53上传目录独立出去
坑放在 webapps 里,重新部署 war 时上传的文件全被清掉
54时区与编码
坑只设 file.encoding 不设 timezone,日志时间还是差 8 小时
Tomcat 假死最难的地方是:进程在、端口通、日志还停在一小时前。这时候唯一能问出真话的是线程栈。
55看 Java 进程与参数
坑jps 默认看不到别的用户启动的进程,用 -lvm 或 ps -ef 补
56看堆与 GC 走势
坑看 OU(已用)而不是 O(容量),只看容量会漏掉快满的事实
57抓线程栈
坑-F 强制抓会 STW,生产上先试普通模式
58找阻塞线程
坑只看 RUNNABLE 会漏掉真正卡住的,等锁的线程都在 BLOCKED
59数线程总数
坑线程数持续上涨不回落就是泄漏,不是“高峰期正常波动”
60判断是否假死
坑端口通但请求挂住,是线程池满了,不是网络问题
61看 http 线程状态
坑http 线程全在跑、业务线程却在等数据库,方向就该往数据库转
62分清两层线程池
坑堵在出口时 Tomcat 线程全在等,看起来像 Tomcat 的问题
63连接池耗尽
坑大量线程堆在 getConnection,是池太小或连接没释放
64开 GC 日志
坑JDK11 之后这些参数全变了,老参数直接不生效还不报错
65看 Full GC 间隔
坑只看次数不看间隔,一小时一次和一分钟一次完全两回事
66导出堆快照
坑dump 会 STW 几秒到大几十秒,别在业务高峰做
67看对象分布
坑:live 会触发一次 Full GC,线上慎用
68分清 OOM 类型
坑都叫 OutOfMemoryError,方向错了会白查一天
69元空间上限
坑动态生成类会让 Metaspace 只涨不降,不设上限最后吃光物理内存
70堆外内存
坑堆外泄漏 jmap 看不出,要看 pmap 和 NMT
71确认线程泄漏
坑单次快照看不出泄漏,必须有对比
72容器里限制堆
坑老 JDK 不认 cgroup,按宿主机内存算堆,容器直接被 OOM Killer 干掉
没有人是被 OOM 突然杀死的。在它之前,GC 日志已经喊了三天。
73基础参数模板
坑Xms 小于 Xmx 会动态扩堆,扩容时抖动明显,还查不出原因
74为什么 Xms 要等于 Xmx
坑不相等就会反复扩缩容,GC 频率上去,监控上看是“内存曲线像锯齿”
75新生代比例
坑Xmn 和 NewRatio 同时写,以 Xmn 为准,另一个静默失效
76选垃圾回收器
坑JDK8 默认 Parallel、JDK9+ 默认 G1,参数没改但行为全变了
77G1 关键参数
坑暂停目标设太小,G1 会疯狂 Young GC,吞吐反而下降
78栈大小
坑栈小深递归直接 StackOverflow,栈大线程一多就吃光内存
79OOM 自动 dump
坑路径没权限,dump 失败只在日志写一行,事后什么都拿不到
80容器里堆别超过 limit
坑堆设满,加上元空间和堆外,容器必然被 kill,看着像“莫名重启”
81用 jcmd 替代一堆工具
坑JDK8 早期版本 jcmd 不全,还得回去用 jmap/jstat
82Arthas 快速定位
坑attach 会短暂 STW,生产上先确认业务能接受
83类加载冲突
坑同一个包多个版本共存,行为随机,堆栈看起来却完全正常
84YGC 太频繁
坑YGC 频繁不一定是内存小,可能是对象创建速率太高
85内存泄漏定位流程
坑只 dump 一次,看到的都是“正常占用”,没有对比就没有结论
86最常见的泄漏写法
坑缓存没有淘汰策略,本质就是内存泄漏,只是看起来像“业务需要”
87确认参数真的生效
坑启动脚本里 JAVA_OPTS 被覆盖,写了等于没写
88CPU 飙高定位
坑不做 16 进制转换,在 jstack 里永远搜不到那个线程
89动态调日志级别
坑调完忘了改回去,DEBUG 日志几天就把磁盘写满
90参数变更流程
坑一次性改全量,出问题连对照组都没有
线上问题最贵的不是修复,是定位。定位的关键只有一句:把链路切成段,一段一段排除。
91接口 500 但日志没异常
坑异常被全局处理器吞了,日志里只剩一句“系统繁忙”
92超时三层定位
坑只看应用日志会漏掉 Nginx 的 504,两边时间对不上就查不下去
93前端报跨域,其实是网关
坑预检被网关直接 403,浏览器只会显示成跨域
94登录状态丢 Cookie
坑http 环境下带了 Secure,Cookie 根本存不下
95上传失败要三层一起改
坑只改一层就以为好了,另外两层还在拦
96大报文 400
坑JWT 放 header 里,token 一大就 400,还看不出原因
97中文乱码整条链路
坑只在应用里设 UTF-8,Tomcat 没设 URIEncoding,GET 参数照样乱
98时间差 8 小时
坑容器基础镜像默认 UTC,代码里取的时间全错
99内部调用握手失败
坑自签证书过期没人管,报错只有 handshake_failure
100单点登录回调失败
坑Nginx 转 https 后回调地址还是 http,页面直接白屏
101灰度分流
坑灰度机与正式机共用 session,用户状态来回跳
102发布后内存持续上涨
坑把正常预热当泄漏去回滚,白折腾一晚上
103多实例会话不共享
坑请求落到不同机器,用户每点一次就要重新登录
104线上问题最小复现
坑不能复现就改代码,等于赌运气
105变更检查清单
坑只写“已备份”,却没验证能不能恢复,等于没备份
106回滚要比发布快
坑出事时才找旧包,找的过程比故障本身还长
107发布后必看的三个数
坑只看错误率,接口没报错但慢了三倍,等于没看
108故障时间线复盘
坑不记时间线,复盘一定变成互相甩锅
* * *
为什么第 3 步必须绕过 Nginx:它能一刀切开“代理层的问题”和“应用层的问题”,省掉一半的排查时间。
第 3 步通、第 1 步不通,问题在 Nginx;两步都不通,问题在 Tomcat 或 JVM。这个判断只花 3 秒。
如果只能记一个,记哪个?
curl -I -m 3 http://127.0.0.1:8080/不是因为它能解决问题,是因为它能一句话把链路切成两段。
我见过太多“页面 502”的报障,一半时间花在争论是 Nginx 的问题还是应用的问题。绕过代理直连一次,争论立刻结束。
先切开链路,再谈原因。这个顺序比记住一百条命令都值钱。
最后,我是运维行者,在政府项目里做信创运维第 8 年。以前做医疗 HIS 实施,现在主要和达梦、麒麟、国产中间件打交道。
这个号只写两件事:现场踩过的坑,和一个 32 岁技术人的选择。
每周三 20:00 技术,周日 21:00 聊聊人。
评论 (0)