news 2026/9/17 6:56:06

CLOSE_WAIT堆积排查与根治:从定位进程到代码修复的完整实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CLOSE_WAIT堆积排查与根治:从定位进程到代码修复的完整实战指南

印象最深的一次故障发生在周四下午,业务方反馈接口大量超时,登录服务器执行ss -ant一看,输出里密密麻麻全是 CLOSE_WAIT,内存和 CPU 看着都还正常,但新连接就是建立不起来,文件描述符也快被打满。这种场景在稍有规模的线上环境里隔三差五就会出现一次:TCP 连接明明已经断了,进程却始终不释放,一条条连接就这么卡在 CLOSE_WAIT 状态,最后把整个进程拖进泥潭。

这篇文章把我平时排查、应急、修复 CLOSE_WAIT 的整套流程整理出来。重点讲三件事:第一,CLOSE_WAIT 是怎么形成的,进程为什么会被卡住;第二,怎么快速定位到那个持有大量 CLOSE_WAIT 连接的进程,并安全地把它清掉;第三,清完之后怎么从代码和系统层面做根因修复,防止它反复出现。不管是后端开发、运维还是 SRE,遇到这类问题都可以直接照着这套思路操作。

1. CLOSE_WAIT 是怎么产生的,进程为什么会被卡住

1.1 从 TCP 四次挥手看 CLOSE_WAIT 的位置

CLOSE_WAIT 不是一个生僻知识,但很多人对它的理解停留在"四次挥手的一个中间状态"这个层面。真要排查的时候,分不清它和 TIME_WAIT 的差别,就容易走弯路。

正常的 TCP 连接关闭要经过四次挥手。假设客户端是主动关闭方,服务端是被动关闭方:

  1. 客户端调用close(),发送 FIN 报文给服务端,客户端进入 FIN_WAIT_1 状态。
  2. 服务端收到 FIN 之后,内核立刻回复 ACK,同时把连接状态切换为 CLOSE_WAIT。客户端收到 ACK 后进入 FIN_WAIT_2 状态。
  3. 服务端应用层这时候需要调用close(),把剩余数据发完,然后向客户端发送 FIN,自己进入 LAST_ACK 状态。
  4. 客户端收到 FIN 后回复 ACK,进入 TIME_WAIT;服务端收到 ACK 后状态变为 CLOSED,连接彻底关闭。

这里的关键就在第 2 步和第 3 步之间。服务端从内核的角度已经知道客户端不想再发数据了,但是应用进程什么时候调用close(),内核是没办法替它决定的。如果应用层一直不调用close(),这个连接就会永远挂在 CLOSE_WAIT 状态。注意,这里标注的状态名在不同工具里显示略有差异:netstat显示为CLOSE_WAITss显示为CLOSE-WAIT,指的都是同一个东西。

打个比方,CLOSE_WAIT 就像客服接到客户说"我要挂电话了",客服嘴上回应了"好的好的",但手始终没有把听筒放回去。电话线路一直被占用,外面的人打不进来。TCP 连接同理,socket 描述符一直被占用,数量多了之后文件描述符耗尽,新连接自然就建不起来了。

1.2 进程陷入 CLOSE_WAIT 的常见原因

CLOSE_WAIT 出现的直接原因是应用层没有关闭 socket,但背后的代码场景千奇百怪。根据我这些年排查的经验,最常见的几类如下。

第一类是代码里漏写关闭逻辑。比如用原生 Socket 编程,处理完请求后直接 return,没有在 finally 块里关闭输入输出流和 socket。这类问题在快速迭代的项目里很常见,尤其是一个人同时维护多个服务的时候,很容易漏。

第二类是连接池中的连接被对端关闭后没有及时清理。服务端使用连接池(数据库连接池、HTTP 连接池、Redis 连接池等)时,如果对端因为空闲超时或异常主动断开了连接,而连接池里的连接没有被判定为失效,下次从池里取出来继续用的时候,读写就会失败。更麻烦的是,如果框架的回收机制不完善,这些失效连接会一直留在池里占着底层的 fd,状态就会慢慢地从 ESTABLISHED 变成 CLOSE_WAIT。

第三类是工作线程阻塞或线程池耗尽。连接对应的业务处理线程如果卡在某个外部调用上始终不返回,socket 就不会被关闭。这种情况通常伴随其他症状,比如线程池队列积压、接口平均响应时间飙升,CLOSE_WAIT 只是外在表现之一。

第四类是异常处理路径没有覆盖。很多框架或自研代码只处理了正常读取到 EOF 的情况,对于读超时、写失败、心跳超时这些异常分支没有统一调用 close。比如用 Netty 的时候,如果exceptionCaught里只打了日志没有关闭 Channel,连接异常断开后就会处于无人清理的状态。

第五类是进程本身进入异常状态,来不及清理资源。比如进程被某种信号干扰、JVM 发生严重 GC 停顿、或者存在死循环,这些都会导致 socket 没有在预期时间关闭。

不管是哪种原因,一旦 CLOSE_WAIT 数量开始堆积,就意味着进程持有的文件描述符数量在持续增长。当 fd 数量达到进程的ulimit -n上限时,任何新建连接都会失败,这个时候问题就从"个别连接泄漏"升级成"服务不可用"了。所以我遇到 CLOSE_WAIT 堆积的第一反应,不是去改代码,而是先保服务,再查根因。

2. 快速定位:把持有 CLOSE_WAIT 连接的进程揪出来

2.1 命令选型:ss 和 netstat 怎么选

先说结论:优先用ss,没有ss的老系统再用netstatss速度快、输出信息更完整,而且能直接显示进程 PID 和 socket fd 的对应关系。netstat属于 net-tools 套件,很多新系统默认不再安装,但老 CentOS 6、Ubuntu 14 之类的环境里依然常见,遇到只有 netstat 的机器也得会用。

查看 CLOSE_WAIT 连接的命令如下:

ss -antp | grep CLOSE-WAIT

注意ss输出的状态列是CLOSE-WAIT而不是CLOSE_WAIT,带不带下划线要看具体工具。使用netstat的话:

netstat -antp | grep CLOSE_WAIT

-a表示显示所有连接,-n表示不解析主机名和端口名,直接用 IP 和端口号显示,-t限定 TCP 协议,-p显示对应的进程 PID 和名称。这三个选项基本是固定的,少一个都会影响排查效率。

ss-p选项同样可以显示进程信息,但需要 root 权限或者对目标进程有足够的权限。如果没加 sudo,输出可能只显示 IP 而不显示进程,这时候第一件要做的事就是加sudo重新执行。

2.2 先摸清 CLOSE_WAIT 的整体规模

上到服务器第一件事,不是看个别连接,而是先看总量和发展趋势。总量决定了你的处理策略:如果是几十条,可能只是个别连接没释放,可以慢慢查;如果是几千条,说明进程已经病入膏肓,得马上考虑重启。

统计当前 CLOSE_WAIT 数量:

ss -ant | grep -c CLOSE-WAIT # 或者使用 awk 统计状态分布 ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn

第二条会输出所有 TCP 状态的数量分布,一眼就能看出哪种状态占比最高。正常情况下 ESTABLISHED 应该是大头,TIME_WAIT 会有一点,CLOSE_WAIT 应该趋近于 0。如果 CLOSE_WAIT 数量排在第一或者第二,说明一定有问题。

光看一次不够,还要看增长速度。用watch每隔几秒刷一次:

watch -n 5 'ss -ant | grep -c CLOSE-WAIT'

数字持续上涨,说明泄漏正在发生,必须立刻处理。数字稳定不动,说明泄漏源可能已经固定,或者一次性产生了大量坏连接后没有再增加,这种情况虽然没那么紧急,但同样需要清掉。

2.3 从连接反查进程 PID

ss -antp的输出里,每一行的最后一列会带上进程信息,比如:

CLOSE-WAIT 1 0 192.168.1.10:8080 192.168.1.20:52340 users:(("java",pid=23456,fd=67))

users里显示了进程名、PID 和文件描述符编号。看到 PID 之后,可以分析这个 PID 下到底有多少条 CLOSE_WAIT 连接,这能帮你判断影响范围。

按 PID 聚合统计 CLOSE_WAIT 连接数量,可以用下面这条命令:

ss -antp | grep CLOSE-WAIT | grep -oE 'pid=[0-9]+' | sort | uniq -c | sort -rn

grep -oE 'pid=[0-9]+'会把每行里的进程号提取出来,然后sortuniq -c统计每个 PID 出现的次数,最后sort -rn按数量从大到小排序。看到结果之后,你就能锁定到底是哪个进程在大量持有 CLOSE_WAIT 连接。如果某个 PID 独占了几百上千条,基本可以直接判定为问题进程。

如果系统没有grep -oE或者环境受限,也可以换用sed

ss -antp | grep CLOSE-WAIT | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | sort | uniq -c | sort -rn

2.4 确认进程身份,别杀错了

定位到 PID 之后,紧接着要确认这个 PID 到底是什么服务。直接执行:

ps -fp 23456

ps -fp会显示进程的 PID、父进程 PID、启动时间、启动命令等信息。如果启动命令看不出业务归属,可以再看一下完整的启动参数:

cat /proc/23456/cmdline | tr '\0' ' ' echo

/proc/23456/cmdline里保存着进程的完整命令行,参数之间用\0分隔,这里用tr把空字符替换成空格,方便阅读。很多时候一个 Java 进程的命令行里能看到-Dspring.profiles.active=prod这种参数,能帮你快速确认是哪个项目的哪个环境。

进程身份确认完了,还要检查一下这个进程对应的文件描述符占用情况:

ls -l /proc/23456/fd 2>/dev/null | grep socket | wc -l

正常情况下一个服务进程打开几十到几百个 fd 都有可能的,但如果这个数字已经上万,甚至接近ulimit -n的限制,那 CLOSE_WAIT 堆积已经快把进程资源耗尽了。

查看进程的 fd 限制:

cat /proc/23456/limits | grep 'open files'

soft limithard limit都会列出来。如果当前 socket fd 数量已经接近 soft limit,说明新连接随时可能失败。

3. 清除 CLOSE_WAIT 进程的几种实操方案

3.1 明确策略:先保服务,再查根因

CLOSE_WAIT 堆积到影响业务的阶段,最有效的处理方式其实是"重启进程"。原因很简单:CLOSE_WAIT 是应用层不调用close()导致的,内核不会主动帮你回收这些 socket,也没有哪个内核参数能强制清理处于 CLOSE_WAIT 的连接。你唯一能对存量连接做的,就是让持有这些连接的进程退出,由内核统一回收它的所有 fd。

我自己处理线上问题的顺序是:先看服务有没有多节点、能不能摘流量;确认可以的话,直接从负载均衡或者注册中心把这个节点摘掉,然后重启进程,观察 CLOSE_WAIT 数量是否归零;服务恢复之后,再决定要不要保留现场做 root cause 分析。保留现场是个好习惯,但如果洪水已经淹到脖子了,先活下来再说。

3.2 给进程发 SIGTERM 还是直接 SIGKILL

重启进程有两种力度:kill <PID>(默认发 SIGTERM)和kill -9 <PID>(发 SIGKILL)。

先尝试kill <PID>。SIGTERM 是温和的终止信号,进程会收到通知,然后走自己的优雅关闭流程——关闭监听 socket、释放连接、清理线程池。对于大多数服务进程,给它 10 到 30 秒时间就足够了。如果进程响应了优雅关闭,CLOSE_WAIT 连接会随着进程退出消失。

如果kill之后等了很久进程还在,说明进程卡住了,无法自行退出。这时候再kill -9 <PID>强制终止。kill -9是内核直接终止进程,没有任何善后余地,所有 fd 由内核回收。缺点是有可能丢数据,但如果进程已经卡死,这些数据本来也保不住。

这里有一个很多人忽略的操作细节:执行完kill之后,用ps -fp <PID>确认进程真的退出了。在某些异常场景下,进程进入了不可中断的 D 状态,普通 kill 和 kill -9 可能都暂时杀不掉。这种时候只能等内核处理完 IO 或者直接重启服务器,但这种情况在常规的业务服务里很少见,遇到后需要单独判断。

多进程模型的服务要格外小心。比如 Nginx 有 master 进程和 worker 进程,如果你只 kill 了 master,worker 可能还在继续跑;反过来,如果你需要重启整个服务,只 kill 掉一个 worker 也不解决根本问题。所以操作之前看清楚ps -fp <PID>输出的 PPID(父进程 PID),把整个进程组摸清楚。

3.3 杀进程前先摘流量,别把故障扩大

看到这里你可能会想:直接 kill 进程不就完了?不行,线上环境直接 kill 节点务必要先摘流量,否则后果很严重。

假设你有 5 个节点,其中 1 个节点 CLOSE_WAIT 爆了。你不摘流量直接 restart,这一瞬间本来打在这个节点上的请求会连接失败,同时负载均衡把流量重新分配给剩下 4 个节点。如果剩下 4 个节点的容量本来就已经吃紧,这波流量转移很容易把别的节点也拖垮,连锁反应就这么发生了。

正确的操作路径:

  1. 在负载均衡层面禁用该节点,或者从注册中心摘除服务,等待正在进行的请求自然结束(通常等待 30 到 60 秒)。
  2. 确认摘除后该节点已经没有新流量,执行kill <PID>kill -9 <PID>
  3. 用 systemd 或 supervisor 等待进程自动拉起,或者手动启动服务。
  4. 进程起来之后先观察 5 分钟,确认 CLOSE_WAIT 数量没有快速反弹,再重新把节点挂到负载均衡上。

如果是云服务器,也可以在安全组层面临时移除该节点的端口放行,但这属于比较硬核的操作,不是所有环境都允许。最常见的还是从负载均衡和注册中心入手。

3.4 重启后的验证清单

重启只是第一步,验证工作做到位才能确认问题真正解决。

验证第一项:CLOSE_WAIT 数量是否归零或接近归零。

ss -ant | grep -c CLOSE-WAIT

这里有个经验:刚重启完 CLOSE_WAIT 归零是正常现象,但如果几分钟内又开始快速增长,说明业务启动后立刻就有连接产生了,代码层面的泄漏点还在,这时候需要进到第 4 章的根因分析。

验证第二项:文件描述符数量是否恢复正常。

cat /proc/<新PID>/fd | wc -l # 更准确的写法 ls -l /proc/<新PID>/fd | grep socket | wc -l

把这个数字和重启前做一个对比。如果重启前有上万条 CLOSE_WAIT,重启后 socket fd 数量应该回落到正常范围。

验证第三项:业务接口是否恢复。调用几个核心接口,确认响应时间和错误率没有问题。同时看日志里是否还有连接相关报错,比如 "Too many open files" 这类错误。

4. 防止复发:从代码和系统层面做根因修复

4.1 找出泄漏源,别让 CLOSE_WAIT 反复找上门

重启只是救火,真正要解决的是"下一次什么时候再爆"。如果 CLOSE_WAIT 在重启后快速反弹,说明代码里的泄漏点是稳定触发的,必须找到它。这块的操作有点技术含量,但掌握了套路就不难。

Java 服务优先用jstack看线程栈。CLOSE_WAIT 连接对应的线程,要么阻塞在某个 IO 操作上,要么处于空闲等待状态。执行:

jstack <PID> > /tmp/stack.log

然后重点看那些处于 WAITING 或 TIMED_WAITING 状态、并且和 socket 读写相关的线程。如果线程名里带有TomcatNettyHttpClient之类的关键字,就顺着这个方向继续排查。

C/C++ 服务用pstack或者gdb附加到进程上去看调用栈。不过生产环境用 gdb 要非常小心,因为 attach 到进程会触发进程暂停,对延迟敏感的服务可能产生明显抖动。能复制现场或者有 test 环境的话,优先在测试环境模拟。

另一种实用手段是strace。跟踪目标进程的系统调用,观察它执行的closeshutdownreadwrite相关操作:

strace -p <PID> -f -e trace=close,shutdown,read,write

-f会跟踪所有子线程,这对于多线程服务是必须的。观察一段时间后,你可能会发现某个 fd 对应的连接在被对端断开之后,程序并没有执行close,而是跳到了别的处理逻辑。这时候回到代码里,沿着这个 fd 的读写路径去找,基本就能定位到泄漏点。

4.2 代码层面的预防和修复

找到问题代码之后,修复的方向比较固定,但每一条都值得写进项目的代码规范里。

第一条,保证每个 socket 都有明确的关闭路径。无论用原生 Socket 还是各种网络框架,连接的关闭逻辑必须放在finally或者语言的资源管理机制里。Java 用try-with-resources,Python 用with语句,Go 用defer。这样无论正常返回还是抛异常,资源都能被释放。

第二条,警惕连接池。连接池不是一劳永逸的。使用数据库连接池时,要合理配置连接最大空闲时间(比如 HikariCP 的maxLifetimeidleTimeout),设置连接有效性检测(testOnBorrow)。使用 HTTP 连接池时,要配置合适的连接空闲回收策略。连接被对端关闭后,从连接池取出的连接很可能会读写失败,框架层面应该有重试或者自动移除失效连接的机制。

第三条,监控线程池和队列。很多 CLOSE_WAIT 问题其实是线程池饱和导致的连锁反应。请求都堵在队列里,对应连接的 socket 根本轮不到处理,自然不会关闭。所以线程池的拒绝策略、队列大小、最大线程数最好加上监控,线程池活跃度持续偏高的时候提前告警,别等到连接开始泄漏了才发现。

第四条,给连接设置超时参数。读超时、写超时可设置成和业务容忍度匹配的值。TCP 层面的 keepalive 也要开启,需要注意的是 keepalive 不仅要在应用代码里设置SO_KEEPALIVE,还要确认系统层面的参数配置合理。

4.3 调整系统参数能帮上什么忙

很多人以为通过内核参数就能把 CLOSE_WAIT 清掉,这里得说清楚:内核没有直接控制 CLOSE_WAIT 生命周期、强制回收空闲连接的参数。CLOSE_WAIT 这个状态的存在本身就是"告诉应用层该 close 了",内核不会替应用做决定,也没法替应用做决定。

不过有几个参数依然值得调整,它们能让死连接更快被探测到,从源头减少 CLOSE_WAIT 的堆积时间。

# 开启 TCP keepalive 相关的默认参数 net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 30 net.ipv4.tcp_keepalive_probes = 3
  • tcp_keepalive_time:连接空闲多久后开始发送 keepalive 探测包,默认 7200 秒(2 小时),对业务来说太久了,可以调到 600 秒(10 分钟)到 1800 秒(30 分钟)之间。
  • tcp_keepalive_intvl:每次探测包的间隔,默认 75 秒,可以调到 30 秒。
  • tcp_keepalive_probes:连续几次探测包无响应后认为连接已死,默认 9 次,可以调到 3 次。

调完使用sysctl -p生效。这样设置之后,一个假死的连接最多在十几分钟内就会被内核判定为失效并关闭,前提是应用层开启了SO_KEEPALIVE。Java 的Socket需要调用setKeepAlive(true),Netty 也需要显式配置,很多框架默认是不开启的。

另外一个容易被忽略的限制是进程的文件描述符数量:

ulimit -n

如果业务本身就应该持有大量连接,65535 可能不够用,可以在启动脚本里用ulimit -n 1000000调高,或者通过 systemd service 文件里的LimitNOFILE设置。但要注意,调高 fd 限制只是推迟了问题爆发的时间,根本解法还是别让连接泄漏。

4.4 建立 CLOSE_WAIT 的日常监控

CLOSE_WAIT 问题最好在萌芽阶段就被发现,而不是等线上接口超时了才去看。日常监控可以做得比较简单,核心就是统计数量加告警。

最基础的一行命令,配合定时任务或者监控脚本用:

ss -ant | grep -c CLOSE-WAIT

可以写一个简单的 shell 脚本,每 60 秒执行一次,把结果输出到日志,让监控系统采集:

#!/bin/bash while true; do count=$(ss -ant | grep -c CLOSE-WAIT) echo "$(date +'%Y-%m-%d %H:%M:%S') CLOSE_WAIT=$count" >> /var/log/tcp_state_monitor.log if [ "$count" -gt 200 ]; then # 触发告警,可以调用 curl 打到告警平台 echo "CLOSE_WAIT too high: $count" | tee /dev/stderr fi sleep 60 done

胜读阈值设置多少要结合业务来定。一般业务出现两位数 CLOSE_WAIT 不算严重,但短时间内涨到几百甚至上千,就必须立刻介入。如果你用的监控系统支持 Prometheus,也可以采集node_netstat_Tcp_CurrEstab这类指标,再把自定义的 CLOSE_WAIT 指标通过 textfile collector 暴露出来,这属于进阶玩法,适合监控体系比较完善的环境。

5. 常见问题与排查技巧实录

5.1 CLOSE_WAIT 排查速查表

每次处理这类问题都从零开始翻命令,效率太低了。我把常见现象和对应原因整理成一个速查表,排查的时候直接对照。

现象大概率原因优先排查方向
大量 CLOSE_WAIT 集中在一个进程代码中 socket 未正确关闭jstack找阻塞线程,查所有 IO 关闭路径
CLOSE_WAIT 数量缓慢上涨连接池中失效连接未清理检查连接池空闲回收策略与验证机制
重启后 CLOSE_WAIT 快速反弹启动后即有稳定触发泄漏的逻辑抓取新建连接路径,配合 strace 看 close 调用
CLOSE_WAIT 伴随接口超时线程池饱和或外部调用阻塞查看线程池活跃度、等待队列长度
CLOSE_WAIT 伴随 "Too many open files"文件描述符耗尽立即重启进程,并调高 fd 限制
所有 CLOSE_WAIT 的远端 IP 相同某个上游客户端或依赖方异常断开联系对端排查,同时优化本端的异常处理
使用数据库连接池时 CLOSE_WAIT 偏高数据库主动断开了空闲连接调整连接池 maxLifetime,开启连接有效性检测

5.2 我踩过的几个坑

第一个坑是混淆 CLOSE_WAIT 和 TIME_WAIT 的处理手段。早期遇到大量 TIME_WAIT,通过调低tcp_fin_timeout、开启tcp_tw_reuse能缓解。后来遇到 CLOSE_WAIT,我照搬这套思路调了半天内核参数,结果一点用没有。TIME_WAIT 是主动关闭方的问题,CLOSE_WAIT 是被动关闭方的问题,本质上是两码事。CLOSE_WAIT 的解药在应用代码里,不在内核参数里。

第二个坑是ss显示不出进程信息。不是命令写错了,很多时候是因为权限不够。用sudo ss -antp才能看到进程列。在容器部署的环境里更麻烦,容器内执行ss看不到 host 上的进程,得进入 Pod 的 network namespace 或者直接在宿主机上用ss -antp | grep <容器端口>才能反查到进程。

第三个坑是pkill误杀。有一次我想清理某个遗留的异常进程,图省事直接pkill -f 'myservice',结果把另一个名称相似的进程也带走了。从那以后我一直坚持用 PID 精确操作,先拿到 PID,确认进程身份,再 kill。生产环境不建议用pkill -f这种模糊匹配来做清理操作,除非你确定没有同名进程。

第四个坑是 restart 之后观察时间不够。有一次我以为重启后 CLOSE_WAIT 归零就万事大吉了,结果半小时之后又爆了一次。后来才意识到,泄漏路径只在某些业务流量出现的时候才会触发,比如某个定时任务的回调或者某个特定来源的请求。所以重启后要拉长观察窗口,至少观察一个业务高峰周期,确认 CLOSE_WAIT 数量稳定了再撤。

5.3 几个不一定有人告诉你的小技巧

如果你在 CLOSE_WAIT 还大量存在的时候需要定位具体的 fd,有一个组合技巧很实用。先用ss -antp拿到 CLOSE_WAIT 连接对应的fd=67这样的信息,然后执行:

lsof -p <PID> | grep 67u

lsof能看到这个 fd 对应的具体 socket 信息,包括对端 IP 和端口,甚至能看出是连接哪个数据库或者哪个 Redis 实例。这对于判断泄漏来源很有帮助。

如果 CLOSE_WAIT 的远端 IP 高度集中,说明问题很可能不在你这端,而在客户端那端。有个案例是某个客户端 SDK 的版本有 bug,连接用完不主动关闭,导致服务端堆了一堆 CLOSE_WAIT。这时候光改服务端代码没用,要推动客户端升级才能根治。

还有一个小技巧是针对 Java 服务的:在 CLOSE_WAIT 堆积时,先执行jstack -l <PID>把线程栈打下来,再用grep -A 20 'socketRead\|socketAccept\|NioSocket'过滤出和 socket 相关的栈。大部分 Java 网络框架在等待 IO 时都会有特征性的堆栈关键字,你能顺着这个很快锁定是哪个库、哪段逻辑在持有连接。

最后一条经验,写在代码规范里比写在博客里更有用。我后来在团队里定了一条要求:所有网络资源的获取和释放必须成对出现,禁止在非 finally 路径里返回;所有连接池必须配置最大生命周期和空闲回收策略。这其实不是技术多高深,而是很多时候问题就出在"觉得不会有事"的那一两个细节上。CLOSE_WAIT 之所以是线上高频故障,恰恰是因为它平时不显眼,等你发现的时候,往往已经积累到了一个临界点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 6:56:03

Windows AI编程环境从零配置:WSL2、Miniconda、Docker与Codex实践指南

很多年前我一直觉得&#xff0c;在 Windows 上搞 AI 编程环境是件自讨苦吃的事。显卡驱动、Python 版本、各种 Linux 才会有的依赖&#xff0c;能把一个下午耗得干干净净。但最近两年情况真的变了&#xff0c;WSL2 成熟之后&#xff0c;Windows 已经成了我主力 AI 开发机&#…

作者头像 李华
网站建设 2026/9/17 6:55:11

MySQL图书管理系统:数据库原生闭环设计实战

简介&#xff1a;本资源是一份面向高校计算机专业学生及数据库初学者的《图书管理系统数据库设计》完整方案文档&#xff0c;聚焦MySQL关系型数据库在图书馆业务场景中的落地实践。文档系统覆盖需求分析、E-R模型建模、6张核心数据表&#xff08;student/book/borrow/return_ta…

作者头像 李华
网站建设 2026/9/17 6:54:42

STM32F407上FreeRTOS与LwIP深度协同实战指南

1. 这不是“跑个例程”——STM32F407上跑通FreeRTOSLwIP的真实门槛在哪里&#xff1f;你搜“STM32F407 FreeRTOS LwIP移植”&#xff0c;首页全是Keil工程截图、几行初始化代码、一句“已验证可用”。但真正把FreeRTOS和LwIP在STM32F407上稳定跑起来&#xff0c;让HTTP服务器能…

作者头像 李华
网站建设 2026/9/17 6:54:30

RDMA无损网络中PFC配置的五大核心参数与实战技巧

1. 项目背景与核心挑战RDMA&#xff08;远程直接内存访问&#xff09;技术正在成为数据中心网络性能优化的关键利器&#xff0c;而PFC&#xff08;优先级流量控制&#xff09;作为保障RDMA无损网络的核心机制&#xff0c;其配置过程却暗藏玄机。三年前我第一次接触RoCEv2网络部…

作者头像 李华
网站建设 2026/9/17 6:54:29

DevOps时代测试工程师的转型与技能升级

1. 测试角色在DevOps时代的转型挑战十年前我刚入行测试时&#xff0c;手工执行用例、记录缺陷还是主流工作模式。如今在持续交付的浪潮下&#xff0c;测试团队经常面临这样的灵魂拷问&#xff1a;当开发自己就能通过流水线完成部署验证&#xff0c;传统测试工程师的价值该如何体…

作者头像 李华