结论: 服务器错误分四个层级:HTTP 状态码层(4xx 多为客户端问题、5xx 为服务端问题)、系统资源层(内存 OOM、磁盘满、inode 耗尽、文件句柄耗尽)、网络层(Connection refused / timeout / reset)、应用层(PHP Fatal、Java Exception、Python Traceback)。先用状态码确定层级,再查该层级的对应日志,是定位任何服务器错误的最短路径。
面对一个报错,新手最常见的动作是把错误信息整段贴进搜索引擎,然后逐个尝试搜索结果里的命令。这种方式偶尔奏效,但大多数时候浪费时间——因为同一个现象可能来自完全不同的层级:网站返回 500,可能是 PHP 语法错(应用层),也可能是磁盘写满导致 session 无法落盘(系统层);接口超时,可能是后端慢(应用层),也可能是安全组没放行(网络层)。
真正高效的做法是先分层,再归因。本文给出完整的错误分类体系与成因对照表,帮你把"看到报错 → 判断层级 → 找到日志 → 确认成因"压缩到几分钟内完成。修复部分(具体命令与回滚流程)见姊妹篇《服务器错误怎么解决》。
先分层:服务器错误的四个层级
结论:所有服务器错误都可以归入四个层级,层级决定了你该看哪份日志。 误判层级是排查低效的头号原因。
| 层级 | 代表错误 | 典型现象 | 主要日志位置 |
|---|---|---|---|
| HTTP 状态码层 | 400/403/404/429/500/502/503/504 | 浏览器或接口直接返回状态码 | /var/log/nginx/access.log、error.log |
| 系统资源层 | OOM Kill、No space left、Too many open files | 服务莫名消失、写入失败、无法新建连接 | dmesg、/var/log/syslog、journalctl |
| 网络层 | Connection refused / timed out / reset by peer | 连不上、连上就断、时好时坏 | journalctl -u 服务、ss、tcpdump |
| 应用层 | PHP Fatal error、Java Exception、Python Traceback | 页面白屏、接口返回堆栈 | 应用自身日志、php-fpm.log、catalina.out |
判断口诀:有状态码看状态码,没状态码先看进程还在不在。 进程没了 → 系统层;进程在但连不上 → 网络层;进程在且能连上但结果不对 → 应用层。
一条体检脚本:三分钟内确定层级
#!/usr/bin/env bash
# 保存为 /usr/local/bin/err-check.sh,报错第一时间执行
echo "===== 1. 服务是否存活 ====="
systemctl is-active nginx php8.3-fpm mysql 2>/dev/null
echo "===== 2. 资源是否耗尽 ====="
df -hT | grep -vE "tmpfs|overlay"
df -i | grep -vE "tmpfs|overlay"
free -h
echo "===== 3. 是否发生 OOM ====="
dmesg -T 2>/dev/null | grep -i "killed process" | tail -5
echo "===== 4. 端口与连接 ====="
ss -lntp | head -20
echo "===== 5. 最近的错误日志 ====="
tail -n 30 /var/log/nginx/error.log 2>/dev/null
journalctl -p err --since "30 min ago" --no-pager | tail -30把这五组输出一次性保存下来,即使自己解决不了,也能让他人或厂商支持一眼看出故障层级。
HTTP 状态码分类:1xx 到 5xx 各代表什么
结论:状态码首位数字直接划分责任方——4xx 是客户端请求有问题,5xx 是服务端处理失败。 这是最有价值的一条判据,能立刻省掉一半无效排查。
| 类别 | 含义 | 责任方 | 处理方向 |
|---|---|---|---|
| 1xx | 信息性响应,请求已接收、继续处理 | 正常 | 无需处理 |
| 2xx | 成功,200 OK / 201 Created / 204 No Content | 正常 | 无需处理 |
| 3xx | 重定向,需进一步操作 | 配置层 | 检查重定向规则是否成环 |
| 4xx | 客户端错误,请求本身有问题 | 请求方 / 权限配置 | 查 URL、权限、限流、请求格式 |
| 5xx | 服务端错误,请求合法但处理失败 | 服务端 | 查后端服务、资源、日志 |
高频子码逐一说明
| 状态码 | 名称 | 典型成因 |
|---|---|---|
| 301 | Moved Permanently | 永久重定向,常用于 HTTP 跳 HTTPS、带 www 统一;配置错误会导致重定向循环 |
| 302 | Found | 临时重定向,常见于未登录跳转;误用会让 SEO 权重不传递 |
| 304 | Not Modified | 客户端缓存命中,服务端未返回正文,属于正常现象不是错误 |
| 400 | Bad Request | 请求报文格式错误、Header 过大、Cookie 超长、JSON 解析失败 |
| 401 | Unauthorized | 缺少或失效的身份凭证 |
| 403 | Forbidden | 目录无 index 且禁止列目录、文件权限不足、WAF/防火墙拦截、IP 黑名单 |
| 404 | Not Found | 路径不存在、root 配错、伪静态规则缺失、文件未部署 |
| 429 | Too Many Requests | 触发限流,Nginx limit_req 或应用层限流生效 |
| 499 | Client Closed Request | Nginx 扩展码:客户端在服务端响应前断开,多因后端响应太慢或用户主动取消 |
| 500 | Internal Server Error | 后端程序异常:PHP Fatal、权限问题、.htaccess/伪静态规则错误 |
| 502 | Bad Gateway | Nginx 收不到上游有效响应:PHP-FPM 未启动、sock 路径错、上游进程耗尽 |
| 503 | Service Unavailable | 服务不可用:主动维护、上游全部健康检查失败、限流熔断 |
| 504 | Gateway Timeout | 上游超时:后端执行超过 fastcgi_read_timeout/proxy_read_timeout |
# 统计访问日志里的状态码分布,快速定位主要错误类型
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -15
# 只看 5xx 并打印对应 URL
awk '$9 >= 500 {print $9, $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 499 高发说明后端慢,配合 $request_time 一起看
awk '$9 == 499 {print $NF, $7}' /var/log/nginx/access.log | sort -rn | head -10注意 499 不在 HTTP 标准里,它是 Nginx 自定义的:客户端提前关闭连接。大量 499 通常意味着"用户等不及",本质是性能问题而非错误。
系统级错误:资源耗尽的四种典型
结论:系统级错误的共性是"服务进程意外消失或写不进去",且往往没有任何应用层报错。 四种典型:内存 OOM、磁盘空间满、inode 耗尽、文件句柄耗尽。
| 现象 | 成因 | 关键日志 / 判据 | 日志位置 | |
|---|---|---|---|---|
| 进程突然消失,无应用层报错 | 内存不足触发 OOM Killer 杀掉占用最大的进程 | 日志含 Out of memory: Killed process | dmesg -T、/var/log/syslog | |
| 写入失败、MySQL 崩、日志报 No space left | 磁盘分区 100% | df -h 显示 Use% 为 100% | /var/log/syslog、应用日志 | |
| 磁盘显示有空间却仍报 No space left | inode 耗尽(海量小文件) | df -i 显示 IUse% 为 100% | 同左 | |
| 无法建立新连接、报 Too many open files | 文件描述符达到上限 | ulimit -n 与 `lsof \ | wc -l` 接近 | journalctl -u 服务、应用日志 |
| 服务启动报 Address already in use | 端口被占用 | `ss -lntp \ | grep :端口` | journalctl -xeu 服务 |
# 1. 查是否发生 OOM(重点看 Killed process 行)
dmesg -T | grep -i "out of memory" -A 3
journalctl -k | grep -i "killed process"
# 2. 磁盘空间与 inode 分开查(两者会独立耗尽)
df -hT
df -i
# 3. 找占空间最大的目录
du -h --max-depth=1 /var 2>/dev/null | sort -hr | head -10
# 4. 文件句柄使用情况
ulimit -n # 当前 shell 限制
cat /proc/$(pidof nginx | awk '{print $1}')/limits | grep "open files"
lsof -p $(pidof php-fpm8.3 | head -1) 2>/dev/null | wc -l
# 5. 内存与 swap 概览
free -h
cat /sys/fs/cgroup/memory.max 2>/dev/null # 容器内的真实上限容器场景特别注意:Docker/Kubernetes 里 free -h 显示的是宿主机内存,容器真实上限要看 cgroup,OOM 往往由容器内存 limit 触发,而非系统整体内存不足。
网络级错误:refused、timeout 与 reset
结论:三种网络错误的成因互不重叠,看报错词就能直接定性。 refused 是"没人接",timeout 是"没人回",reset 是"被主动断开"。
| 报错 | 字面含义 | 最可能成因 |
|---|---|---|
Connection refused | 目标端口无进程监听 | 服务未启动、监听地址仅为 127.0.0.1、端口写错 |
Connection timed out | 请求发出后无响应 | 安全组/防火墙丢包、IP 不可达、路由错误、上游被黑洞 |
Connection reset by peer | 对端主动发送 RST | 进程崩溃、协议不匹配(HTTP 访问 HTTPS)、上游超时主动断连、WAF 拦截 |
No route to host | 无可用路由 | 网关配置错误、目标 IP 不存在、本机路由表损坏 |
Name or service not known | 域名解析失败 | /etc/resolv.conf 配错、DNS 服务不可达 |
Temporary failure in name resolution | 解析临时失败 | systemd-resolved 异常或 DNS 服务器超时 |
# 1. 区分 refused 与 timeout(同一条命令两种结果含义完全不同)
nc -zv -w 3 127.0.0.1 9000 # refused: 端口无监听
nc -zv -w 3 8.8.8.8 443 # timeout: 出方向被封
# 2. 确认服务监听地址(0.0.0.0 才对外可见)
ss -lntp
# 3. 抓包确认是否收到请求(判断丢包发生在哪一侧)
tcpdump -i any -nn host <客户端IP> and port 443 -c 20
# 4. 查内核丢包与连接追踪
nstat -az | grep -iE "TcpExtListenDrops|TcpExtListenOverflows"
conntrack -S 2>/dev/null应用层错误:PHP Fatal、Java Exception、Python Traceback
结论:应用层错误一定能拿到堆栈或错误行,这是最容易定位的一类——日志里通常直接写了文件名与行号。
| 语言 / 运行时 | 典型错误文本 | 常见成因 | 日志位置 |
|---|---|---|---|
| PHP 8.3 | PHP Fatal error: Uncaught Error: Call to undefined function | 扩展未安装、函数被 disable_functions 禁用、版本升级弃用函数 | /var/log/php8.3-fpm.log、Nginx error.log |
| PHP 8.3 | PHP Fatal error: Allowed memory size ... exhausted | memory_limit 过小或代码内存泄漏 | 同上 |
| PHP-FPM | Primary script unknown | SCRIPT_FILENAME 路径错、文件不存在、open_basedir 限制 | Nginx error.log |
| Java(Spring Boot / Tomcat) | java.lang.NullPointerException、OutOfMemoryError: Java heap space | 空指针未判空、堆内存不足或内存泄漏 | catalina.out、应用 logs/ |
| Python(Django / Flask) | Traceback (most recent call last) + 具体异常 | 依赖缺失、数据库连不上、代码逻辑异常 | gunicorn/uwsgi 日志、应用日志文件 |
| Node.js | UnhandledPromiseRejection、EADDRINUSE | 异步未捕获异常、端口被占用 | PM2 日志、应用日志 |
# PHP:实时看错误日志
tail -f /var/log/nginx/error.log
tail -f /var/log/php8.3-fpm.log
# Java:抓异常堆栈
grep -n "Exception\|Error" /opt/tomcat/logs/catalina.out | tail -30
jstack > /tmp/jstack.txt # 线程栈,用于分析卡死
# Python:定位 traceback
journalctl -u gunicorn --since "10 min ago" | grep -n "Traceback" -A 20 一条通用的"现象 → 成因 → 日志"对照表
| 现象 | 最可能成因 | 第一份该看的日志 |
|---|---|---|
| 整站 500,重启后短暂恢复 | PHP-FPM 进程耗尽或 OOM | /var/log/php8.3-fpm.log + dmesg -T |
| 接口偶发 504 | 慢 SQL、上游超时阈值过小 | Nginx error.log(upstream timed out)+ 数据库慢日志 |
| 上传文件失败 | 目录不可写、client_max_body_size 过小 | Nginx error.log |
| 登录后立即掉线 | session 目录满或权限错、Redis 连接失败 | 应用日志 + df -h |
| 静态资源 404 | root 路径错、文件权限 600 | Nginx error.log(含 open() failed) |
| HTTPS 提示不安全 | 证书过期、域名不匹配、缺中间证书 | openssl s_client 输出 |
常见误区 / 排错提示
- 看到 500 就重启服务:重启能掩盖但不能解决问题,且会丢失现场。应先保存
error.log相关片段再操作。 - 把 4xx 当成服务器故障:403/404/429 绝大多数是配置或请求问题,改服务器端参数无效,要查权限、路径与限流规则。
- 只看应用层日志不看
dmesg:OOM Kill 不会写进任何应用日志,只有dmesg -T能看到Killed process。 df -h有空间就认为磁盘没问题:inode 可能已耗尽,df -i才是完整判断,海量小文件(session、缓存碎片)最容易触发。- 把 499 当错误码修:499 是客户端提前断开,本质是后端响应慢,优化性能才会真正消失。
- 容器内用
free -h判断内存:容器里看到的是宿主机总量,真实上限在 cgroup,误判会导致 OOM 反复发生。 - 改配置不验证语法:Nginx 与 PHP-FPM 都提供
nginx -t与php-fpm8.3 -t,未验证就 reload 可能直接让服务停摆。
常见问题(FAQ)
4xx 和 5xx 错误有什么本质区别?
结论:4xx 表示客户端请求本身有问题,服务端拒绝处理;5xx 表示请求合法但服务端处理失败。 4xx 常见如 400 报文格式错、401 未认证、403 无权限、404 路径不存在、429 触发限流,责任在请求方或权限配置;5xx 常见如 500 程序异常、502 上游无响应、503 服务不可用、504 上游超时,责任在服务端。这一区分决定了排查方向:4xx 查 URL、权限与限流规则,5xx 查后端进程、资源与日志。
502 和 504 有什么区别?
结论:502 是上游没给有效响应(连接不上或返回非法内容),504 是上游响应太慢超时。 502 常见于 PHP-FPM 未启动、sock 路径配置错误、进程池耗尽,日志多为 connect() failed;504 常见于慢 SQL、外部 API 阻塞、后端执行时间超过 fastcgi_read_timeout(Nginx 默认 60 秒)。处理方向不同:502 要恢复或扩容上游进程,504 要优化慢查询或调大超时阈值,两者不能互相替代。
服务器进程莫名消失是什么原因?
结论:最常见是内存不足触发 OOM Killer,其次是 systemd 重启策略或进程自身崩溃退出。 确认方法是 dmesg -T | grep -i "out of memory",若能看到 Killed process 即可确认为 OOM。其他可能:容器内存 limit 超限(看 cgroup 而非 free)、应用自身 exit、被 kill -9 或运维脚本误杀、watchdog 判定无响应后重启。定位后应保留 dmesg 与 journalctl -u 服务 输出,再决定是加内存还是限流。
磁盘还有空间却提示 No space left on device 是怎么回事?
结论:这是 inode 耗尽,不是空间耗尽——文件数量达到上限,即使还有剩余容量也无法新建文件。 用 df -i 查看 IUse%,为 100% 即确认。高发场景是海量小文件:PHP session 碎片、缓存目录未清理、邮件队列堆积、日志轮转失效。处理方式是定位小文件密集目录(find /path -type f | wc -l),清理无用文件,并为日志配置 logrotate 定期轮转,避免复发。
499 状态码是什么意思,需要修吗?
结论:499 是 Nginx 自定义状态码,表示客户端在服务端返回响应前主动断开连接,标准 HTTP 规范中不存在该码。 它通常意味着用户等待超时后关闭页面,本质是后端响应慢,而不是一个"错误"。处理方式不是改状态码逻辑,而是优化性能:查慢 SQL、调大 PHP-FPM 进程数、加缓存或 CDN。可用 awk '$9 == 499' 统计高发 URL,配合 $request_time 字段找出最慢的接口。
接口时好时坏属于哪一类错误?
结论:间歇性失败多属于网络层或资源层的临界状态,而非应用层逻辑错误。 三种高频成因:一是连接池/进程池打满,高峰期请求排队超时;二是网络链路丢包或某运营商节点劣化,用 mtr -r -c 30 观察丢包率;三是上游服务部分实例不健康,负载均衡把请求打到了病态节点。判断方法是对比失败时间点与监控曲线(CPU、内存、连接数、磁盘 IO),若与高峰重合则为容量问题,否则查链路与健康检查。
企业QQ咨询




