主题
界面
主题
《Linux 服务部署与运维》让服务"能跑起来"。本节解决**"起不来 / 连不上 / 卡住了"**:
"端口被占用,启动失败" → 查端口
"服务在跑,但请求连不上" → 查监听地址 / 防火墙
"CPU 100%,谁在跑?" → 查进程与线程
"进程还在,但没响应" → 查线程栈这是一节"排查"课 ——目标是把"现象"翻译成"原因" ✓
ps 的输出(PID / PPID / COMMAND)★;top 看 CPU / 内存 / 负载,知道"负载高"意味着什么 ★;jps / jstack / jstat)★★;三个高频现象,排查命令完全不同 ✓
| 现象 | 根因方向 |
|---|---|
Address already in use | 端口被别的进程占了 ✗ |
Connection refused | 没在监听(没启动 / 只监听 127.0.0.1 / 防火墙)✗ |
| CPU 100% | 死循环、频繁 GC ✗ |
本节要建立的就是"现象 → 命令"的映射 ✓
ps | 概念 | 说明 |
|---|---|
| PID | 进程 ID ★ |
| PPID | 父进程 ID(进程树)✓ |
| UID | 以谁的身份运行 ★ |
常用写法:
ps -ef # 全格式
ps -ef | grep java # ★ 找 java 进程
ps -ef | grep [j]ava # ★ 巧妙:不匹配 grep 自己
ps -o pid,etime,rss,cmd -p <pid> # ★ 看某个进程:跑了多久、占多少内存实测(Git Bash):
UID PID PPID TTY STIME COMMAND
testuser 76 1 ? 16:34:12 /usr/bin/bash -c cd /c/project/...
testuser 80 76 ? 16:34:14 ps -ef★ COMMAND 列最有价值:看到 PID 后一定要看它 ——java -jar /opt/order/app.jar --spring.profiles.active=prod 能一眼看出"哪个应用、哪个环境" ✓
Linux:
ss -tlnp | grep :8080 # ★ 推荐(更快)
netstat -tlnp | grep :8080
lsof -i :8080| 选项 | 含义 |
|---|---|
-t | TCP |
-l | 只显示监听中的 ★ |
-n | 不解析域名(快)✓ |
-p ★ | 显示进程(关键)✓ |
实测(本机 netstat -ano,Windows 用 -o 显示 PID):
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 1724
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:5040 0.0.0.0:0 LISTENING 15472
TCP 0.0.0.0:5432 0.0.0.0:0 LISTENING 6704
TCP 0.0.0.0:5985 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:9593 0.0.0.0:0 LISTENING 4320
监听数量:69| 列 | 含义 |
|---|---|
| 本地地址 | 0.0.0.0:5432 = 监听所有网卡的 5432 ✓ |
| 状态 | LISTENING = 在等别人来连 ✓ |
| PID ★ | 占用该端口的进程 ✓ |
★ 5432 是 PostgreSQL 的默认端口 ——与 《MySQL 搭建与客户端使用》 里"本机跑着 PostgreSQL"完全对上 ✓
拿到 PID 后:ps -o pid,etime,rss,cmd -p 6704 —— 这就是"端口 → PID → 进程"的完整链路 ✓
现象:启动报 "Port 8080 was already in use"
① 谁占了? ss -tlnp | grep :8080 → 得到 PID
② 它是什么? ps -o pid,etime,cmd -p <PID> → ★ 看 COMMAND
③ 该不该结束?
├─ 上次没关干净的自己的应用 → 可以结束 ✓
├─ 别的服务 → ★ 不要随便 kill
└─ 系统进程 → ✗ 绝不动
④ 先礼貌:kill <PID>(SIGTERM);等几秒;还在才 kill -9
⑤ 重启自己的服务★ 第 ③ 步最重要:"看到 PID 就 kill"是危险的 ——你可能杀掉别人的服务 ✗✗
更稳妥的替代:换端口 ✓
java -jar app.jar --server.port=8081(尤其在共享的测试服务器上 ✓)
| 状态 | 含义 | 排查价值 |
|---|---|---|
LISTENING | 在监听 ✓ | "服务在不在"看这个 ★ |
ESTABLISHED | 已建立连接 | 有多少客户端连着 ✓ |
TIME_WAIT | 刚关闭,等待 | 多通常正常 ✓ |
CLOSE_WAIT ⚠️ | 对方关了,我方没关 ★ | 多 = 连接泄漏 ✗✗ |
★ CLOSE_WAIT 是"连接泄漏"的信号:应用没调用 close() → 描述符耗尽 → 无法建立新连接 ✗
统计各状态:
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rntop 与负载 top # 实时;top -p <pid> 只看某个 ★
uptime # ★ load average
free -h # 内存
df -h # 磁盘★ load average(1/5/15 分钟):"正在运行 + 排队等待"的任务数 ✓
load 4.0 且 2 核 → 有一半在排队 → 慢 ⚠️
判断标准:load / CPU 核数 ≈ 1 是健康上限 ✓⚠️ 一个常见误解:CPU 高 ≠ 负载高:
| 情况 | 含义 |
|---|---|
| CPU 高、load 低 | 一个进程在算(可能正常)✓ |
| CPU 低、load 高 ★ | 大量任务在等(等 IO / 等锁)——更隐蔽 ✗ |
| 工具 | 作用 | 用法 |
|---|---|---|
jps ★ | 列出 Java 进程 | jps -l ✓ |
jstack ★★★ | 线程栈 | jstack <pid> > /tmp/thread.txt ✓ |
jstat ★★ | GC 统计 | jstat -gcutil <pid> 1000 10 ✓ |
jmap | 内存直方图 / 堆转储 | jmap -histo:live <pid> ⚠️ 会触发 GC |
★ jstack 是排查"卡住 / CPU 高"的第一工具:看有没有大量 BLOCKED(等锁)、有没有线程一直停在同一个方法(死循环)。
好习惯:CPU 高时连续抓 3 次 jstack(间隔几秒) ——三次都在同一个栈的线程就是嫌疑犯 ✓
jstat 看 GC:FGC/FGCT 频繁增长 = Full GC 频繁(内存泄漏 / 堆太小)✗
| 现象 | 先查 | 命令 | 可能的结论 |
|---|---|---|---|
| 端口被占用 | 谁占着 | ss -tlnp | grep :<port> | 旧进程没关 / 别的服务 |
| Connection refused | 在监听吗 | ss -tlnp | 没监听(没启动 / 只监听 127.0.0.1)✗ |
| 本机通、外部不通 | 监听地址 + 防火墙 | ss -tlnp;iptables -L | 只监听 127.0.0.1 ✗ 或防火墙 |
| 响应慢 | 负载 / GC / 慢查询 | uptime;jstat;慢查询 | CPU?GC?SQL? |
| CPU 100% | 进程 → 线程 | top → jstack | 死循环 / 频繁 GC |
| 内存一直涨 | 堆占用 | jstat -gcutil / jmap | 内存泄漏 |
| 连接数暴涨 | 状态分布 | ss -tan | ... | uniq -c | CLOSE_WAIT 多 = 没关连接 ✗ |
| 进程在但没响应 | 线程栈 | jstack | 线程池满 / 死锁 ✗ |
| 磁盘满 | 哪个目录 | df -h → du -sh * | 日志没轮转 ✗ |
★ 排查时优先"保留现场":重启前先抓 jstack、jstat、日志、连接状态 ✓(《Linux 服务部署与运维》 强调过)
ss -tlnp | grep :8080
ps -o pid,etime,cmd -p <PID>① ./start.sh → "Port 8080 was already in use"
② ss -tlnp | grep :8080 → users:(("java",pid=12345,...))
③ ps -o pid,etime,cmd -p 12345 → java -jar /opt/order/app.jar(跑了 3 天)
→ 是"三天前忘了关的旧版本" ✓
④ kill 12345(等它优雅退出)
⑤ ./start.sh → 成功 ✓kill -9 风险:如果那是别人的服务,你制造了一次事故 ✗✗
修:先看 COMMAND(5.3 的第 ③ 步)✓
环境说明:本机 Windows + Git Bash。
ps、netstat、端口→PID 可实测 ✓;ss/lsof是 Linux 工具(Windows 用netstat -ano代替);jstack需要 JDK ✓
实测(一)进程:
ps -ef | head -5 # 见 5.1:有 UID / PID / PPID / COMMAND实测(二)端口与 PID:
netstat -ano | grep -i "LISTENING" | head -6 # → 5432 的 PID 是 6704
netstat -ano | grep -ci "LISTENING" # → 69实测(三)用 PID 检查存活(《Linux 服务部署与运维》 脚本里的用法):
cat /tmp/demo-service/app.pid # → 1835
kill -0 1835 && echo "进程在 ✓" # ★ kill -0 只检查存在性,不发信号结论:
| 实测 | 结论 |
|---|---|
5432 → PID 6704 ★ | 端口能反查进程("端口被占用"的解法)✓ |
ps -ef 有 PPID 与 COMMAND | 能看到"谁启动的、跑了什么" ✓ |
kill -0 | 脚本里判断存活的标准写法 ✓ |
LISTENING vs ESTABLISHED:连一个本地服务再查,观察状态变化 ✓;ps -ef | grep java 会看到 grep 自己:用 grep [j]ava 对比 ✓;kill -0 练习:启动进程 → 检查 → 杀掉 → 再检查 ✓;ss -tlnp 与 netstat -tlnp 对比(ss 更快)✓。| # | 错误 | 说明 |
|---|---|---|
| 1 | 不看 COMMAND 就 kill ✗✗ | 可能杀掉别人的服务 |
| 2 | 连不上就以为"服务挂了" | 可能是只监听 127.0.0.1 ✗(server.address=0.0.0.0) |
| 3 | 只看 CPU 不看 load | CPU 低 + load 高 = 在等(IO/锁)★ |
| 4 | 重启前不抓现场 | 原因消失,下次还会来 ✗ |
| 5 | kill -9 当常规手段 | 破坏优雅停机(《Linux 服务部署与运维》)✗ |
| 6 | 忽略 CLOSE_WAIT | 它是"连接泄漏"的信号 ✗ |
| 7 | 把 TIME_WAIT 当问题 | 通常正常;真正该警惕的是 CLOSE_WAIT ✓ |
ps 查它是什么 ✓;deploy-demo.sh,用 ps 找到它,用 kill -0 检查,最后停止 ✓;deploy-demo.sh,观察第二次的行为(它应该报"已在运行")✓;ss -tlnp 做一遍 ✓。扩展任务:写 port.sh <端口号>:输出占用者的 PID/用户/启动时间/命令行;没人占用就输出"空闲";退出码:占用=0、空闲=1(可被其他脚本调用)★。
基础题
LISTENING 表示"已经和客户端建立了连接"。(对 / 错)CLOSE_WAIT 持续增长意味着什么?代码阅读题
$ ss -tlnp | grep :8080
tcp LISTEN 0 100 127.0.0.1:8080 0.0.0.0:* users:(("java",pid=4321,fd=52))编程题
[OK]/[问题];有问题就 exit 1;用函数组织;set -uo pipefail(不用 -e)。回答:为什么要一次检查所有项?综合任务
基础题
错。LISTENING = 在等别人来连;ESTABLISHED 才是已建立连接 ✓ (推论:进程在 ≠ 在监听 —— 可能启动了但初始化失败,所以"服务在不在"要看端口)★
ss -tlnp | grep :8080;ps -o pid,etime,cmd -p <PID> ✓
应用没正确关闭 socket(连接泄漏) ✗ → 描述符耗尽 → 无法建新连接 → 假死 ✗✗
代码阅读题
java 进程(PID 4321)只监听 127.0.0.1:8080 ⚠️ —— 只有服务器自己能连 ✗
最可能是只监听了 127.0.0.1(特征就是"本机通、外部不通")✓ 修:server.address=0.0.0.0,改完用 ss -tlnp 确认变成 0.0.0.0:8080 ✓ 如果监听地址没问题 → 再查防火墙/云安全组 ✓ 顺序:① 看监听地址 ② 本机 curl ③ 外部 curl ④ 防火墙 —— 由内到外 ✓
编程题
-e(要检查所有项);curl -f --max-time(避免卡死);ss 失败时 netstat 兜底;exit 1 接监控 ✓ 为什么检查所有项:很多时候"多个问题"其实是"一个根因的多个表现"(如磁盘满 → 日志写不了 → 健康检查失败)。只报第一个问题会让人去修症状 ✗综合任务
| 原因 | 命令 | 会看到 |
|---|---|---|
| 线程池耗尽 ★ | jstack | 大量线程 WAITING 在队列 |
| 连接池耗尽 ★ | jstack | 线程卡在 getConnection |
| 死锁 | jstack | "Found one Java-level deadlock" ★ |
| 等外部依赖 | jstack / 日志 | 卡在 socketRead0;日志有 timeout |
| GC 停顿 | jstat -gcutil | FGC/FGCT 大 |
| 等磁盘 IO | top(看 wa) | CPU 低但 wa 高;进程状态 D |
排查顺序:① jstack 抓 3 次(一次能排除死锁/线程池/连接池/等外部)→ ② jstat(排除 GC)→ ③ top(看 wa 与 load)→ ④ ss(连接堆积)→ ⑤ 日志与依赖方 ✓
为什么"CPU 低却假死"更值得警惕:
jstack 一眼看出在跑什么)✓"假死"几乎都源于"等待没有上限"。防御方式就是:给每一个"等"都设一个上限。 ✓
ps -ef 的每一列(尤其 COMMAND)★;ss -tlnp / netstat 由端口反查进程 ★★★;LISTENING 与 ESTABLISHED,知道 CLOSE_WAIT 的含义 ★;top / uptime,理解 load 与 CPU 的关系 ★;jps / jstack / jstat ★★;Linux 五节(《Linux 常用命令与部署》 / 《Linux 文件、用户与权限管理》 / 《Linux 服务部署与运维》 / 《Shell 脚本基础》 / 《进程与端口排查》)到此完成。接下来可以:
暂无