把 socket 设成非阻塞后,recv 频繁抛 BlockingIOError: [Errno 11] Resource temporarily unavailable,进程按异常退出,10 分钟内重启了 14 次。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

try:
    data = sock.recv(4096)
except BlockingIOError:
    continue

非阻塞模式下没有数据可读时内核返回 EAGAIN,这是正常状态而不是错误。代码把所有 OSError 都当成致命异常,遇到一次空读就退出。这一点先确认清楚,后面就不用反复试。

「EAGAIN 处理」这个问题文档写得含糊,实际行为以当前版本为准,别照旧版本的结论套。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。

改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。验证用真实数据走一遍,测试数据太干净,边界情况根本碰不到。

能分两步做的操作就别合成一步,中间看一眼返回值,出问题好判断。

具体做法是,单独捕获 BlockingIOError 并继续循环;用 select 或 epoll 等可读事件;发送方向的 EAGAIN 要保留待发数据;其余 errno 仍然按异常处理。

改完照着核一遍,进程连续运行 12 小时没有因为 Errno 11 退出,空转时 CPU 占用低于 3%。

作者 9823189