一个连接断开后对应线程的 CPU 稳定在 100%,strace 显示 recvfrom 每秒返回 0 上百万次,日志里没有任何异常。日志里能对上时间点,这种反而好排。

recv 返回 0 表示对端已经关闭写方向,不是暂时没有数据。代码只把 -1 当成错误,0 被当成正常继续循环,形成空转。这一点先确认清楚,后面就不用反复试。

n = sock.recv(4096)
if n == 0:
    epoll.unregister(fd)
    sock.close()

「recv 返回 0」改完先别急着全量,挑一台机器放半天,真有问题这段时间就能看出来。先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。

别只看报错的最后一行,往上看几行,真正的起因通常在上面。同类问题如果隔一段时间又出现,多半是某个定时任务或重启流程把改动冲掉了。

把这次用到的命令存进笔记,比记住结论有用,换个环境还能照着走。

处理上按这个顺序:把返回 0 当成连接结束处理;关闭 fd 并清理连接状态;从 epoll 里摘掉这个 fd;统计半关闭连接数便于发现异常。

结果对不对很好判断,修复后断开连接不再占 CPU,进程整体占用从 100% 降到 3%,epoll 里没有残留的已关闭连接。

作者 9823189