Python使用smtplib无需SMTP认证发邮件延迟10秒问题排查
延迟问题的核心定位
- 对接无需强制认证的SMTP服务器时,通过smtplib无认证直接发信会产生累计约10秒的固定延迟;走显式登录流程发信则耗时极短。
- 同场景下使用SwithcMail、mailsend-go等命令行工具,哪怕不做认证流程也能快速发信,说明问题不是服务器侧的全局规则,是smtplib的交互逻辑和服务器反垃圾规则叠加触发的。
- 调试日志已经明确延迟来源:执行
rcpt TO命令阶段,smtplib会额外发出2次收件人为空地址的请求。未认证状态下,服务器对这类无效请求的响应固定延迟5秒/次,累计产生10秒等待;认证状态下服务器对这类无效请求无延迟返回,整体发信速度不受影响。
根因说明
smtplib的sendmail方法会逐一遍历传入的收件人列表,将每个列表元素作为独立的rcpt TO命令发送,不会自动过滤列表中的空值(None、空字符串、空白字符串)。你日志里出现的两次rcpt TO:<>(空地址)请求,就对应你代码中拼接的RECIPIENTS_ALL列表里存在2个空值元素,大概率是mail_bcc变量默认返回空列表/空字符串导致的。
未认证场景下的5秒固定响应延迟,是SMTP服务器的通用反垃圾设计:
对未通过认证的不可信连接,服务器收到空地址、明显异常的请求时,会触发tarpit(泥坑延迟)机制,故意拉长响应时间,提高批量发送垃圾邮件的成本;对已经完成认证的可信连接,服务器会直接返回错误码,不附加延迟。
这也解释了为什么第三方命令行工具不会遇到该问题:这类工具在发送前会自动过滤收件人列表中的空值,不会主动发送容易触发反垃圾规则的无效请求。
可落地的优化方案
- 从根源过滤收件人列表的无效值,从源头避免空
rcpt TO请求的发送。将原来的列表拼接逻辑替换为过滤后的版本:
RECIPIENTS_ALL = [ addr for addr in (mail_to + mail_cc + mail_bcc) if addr and isinstance(addr, str) and addr.strip() ]
- 核对现有调试日志中,空地址
<>的请求数量,和过滤后剩余的收件人数量,确认空值全部被清除后,延迟会直接消失。 - 精简冗余调用:smtplib的
starttls()方法本身会在TLS通道建立完成后自动重新发送EHLO命令,代码中手动重复调用ehlo()属于冗余操作,可直接移除。 - 若业务上不需要发送密送邮件,可直接去掉和
mail_bcc相关的拼接逻辑,进一步减少不必要的请求步骤。
内容的提问来源于stack exchange,提问作者Hermie
相关产品推荐
相关产品推荐

