You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于asyncore的SMTPServer套接字未关闭致文件描述符持续增长问题排查

根本原因分析及排查方向

从你的描述和代码来看,套接字描述符持续增长的问题,核心大概率和Python标准库smtpd.SMTPServer的连接回收机制缺陷以及特定客户端的异常断开行为有关,具体可以拆成这几个关键点:


1. smtpd依赖的asyncore框架对异常断开的处理不足

smtpd.SMTPServer是基于asyncore实现的异步网络服务,它的连接回收逻辑严重依赖客户端发送的正常关闭信号(比如SMTP的QUIT命令,或者TCP层的FIN包)。

但线上的邮件服务商场景很复杂:

  • 有些服务商可能使用长连接池,但连接闲置后没有主动发送QUIT就直接断开;
  • 部分情况下可能出现网络波动、服务器崩溃,导致TCP连接变成半开状态(服务器端认为连接还存活,但客户端已经离线);
  • 还有些客户端可能发送不符合SMTP规范的数据,导致解析阶段出错,asyncore没有触发连接关闭逻辑。

而你本地测试时,客户端进程退出会主动发送FIN包,asyncore能正确识别并回收FD,所以不会出现问题,但线上这些异常场景就会导致套接字残留。

2. 你的异常捕获范围只覆盖了邮件处理环节,没覆盖连接生命周期全程

你在process_message里用try-except包裹了业务逻辑,但smtpd处理连接的流程远不止这一步:从接收客户端连接、解析SMTP命令(比如EHLO/MAIL FROM/RCPT TO)到最终关闭连接,任何一个环节抛出未被捕获的异常,都可能导致asyncore无法正常关闭套接字。

比如,如果客户端发送了格式错误的RCPT TO命令,在process_message执行之前的解析阶段就抛出异常,这时候你的try-except根本捕获不到,连接就会被留在那里。

3. 未启用TCP Keepalive检测死连接

默认情况下,TCP连接不会主动检测对方是否存活。如果某个邮件服务商的连接意外断开(比如机房断电),你的服务器端套接字会一直处于ESTABLISHED状态,直到系统的TCP超时(通常是几十分钟甚至几小时)才会回收,这期间FD会一直被占用。


快速验证方法

你可以用ss -tp | grep <你的进程PID>查看这些残留套接字的状态:

  • 如果大量处于ESTABLISHED但没有数据传输,大概率是半开连接;
  • 如果是CLOSE_WAIT状态,说明客户端已经关闭,但你的服务器端没有调用close()。

建议的修复方向

这些不是临时补丁,而是从根本上解决问题的方案:

  • 重写smtpd的连接处理逻辑:继承SMTPServer后,重写handle_close、handle_error方法,确保无论是否出现异常,都主动关闭套接字;
  • 给服务器套接字添加TCP Keepalive:在__init__里获取底层套接字,设置SO_KEEPALIVE相关选项,让系统定期检测死连接:
    import socket
    
    class MyMXServer(SMTPServer):
        def __init__(self, host='127.0.0.1', port=25):
            self.srs = SRS.new(os.getenv('SRS_KEY', 'some passwd'))
            super().__init__((host, port), None)
            # 启用TCP Keepalive
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
            # 设置检测间隔(以Linux为例,不同系统参数可能有差异)
            self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 300)  # 5分钟无数据开始检测
            self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 60)  # 每隔1分钟检测一次
            self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)     # 检测5次失败则关闭连接
    
  • 替换smtpd模块:如果对稳定性要求高,建议用更现代的异步框架(比如aiosmtpd),它们的连接管理机制更健壮,原生支持超时和连接回收。

内容的提问来源于stack exchange,提问作者Cyril N.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:40:03