简介
本文档描述了 Postfix 连接缓存的实现,该功能自 Postfix 2.2 版本起可用。
本文档涵盖的主题:
SMTP 连接缓存能为您带来什么
通过 SMTP 连接缓存,Postfix 可以通过同一 SMTP 连接发送多条消息。默认情况下,Postfix 2.2 会自动复用一个明文 SMTP 连接,当目标的 活动队列 中邮件量较大时。
SMTP 连接缓存是一项性能优化功能。其实际性能提升效果取决于具体条件:
- 当向拥有多个邮件服务器的目的地发送邮件时,SMTP 连接缓存可显著提升性能,因为它能帮助 Postfix 跳过未响应的服务器。
- SMTP 连接缓存还能帮助应对对新连接设置速率限制的接收方。
- 否则,SMTP 连接缓存的优势微乎其微:它消除了 TCP 握手(SYN、SYN+ACK、ACK)的延迟,以及 SMTP 初始握手(220 问候、EHLO 命令、EHLO 响应)的延迟。对于 TLS 加密连接,这可额外节省两个往返时间,否则需要发送 STARTTLS 并恢复 TLS 会话。
- SMTP 连接缓存对 SMTP 会话关闭没有提升。Postfix 的 smtp(8) 客户端通常不会等待服务器对 QUIT 命令的响应,也不会等待 TCP 最终握手完成。
- SMTP 连接缓存会引入一些开销:客户端需要发送一个 RSET 命令来确定连接是否仍可使用,然后才能发送下一个 MAIL FROM 命令。这会引入一次额外的往返延迟。
有关 SMTP 连接缓存的其他潜在问题,请参阅本文末尾的 限制 部分。
连接缓存实现
有关 Postfix 邮件投递的概述,请参阅 Postfix 架构 OVERVIEW 文档。
Postfix 连接缓存由 Postfix 邮件投递进程共享。这最大限度地提高了重用打开连接的机会。某些 MTA(如 Sendmail)具有非共享连接缓存。在此情况下,连接只能由创建该连接的邮件投递进程重用。为了获得与共享连接缓存相同的性能提升,非共享连接需要保持打开状态更长时间。
scache(8) 服务器(在 Postfix 2.2 版本中引入)维护共享连接缓存。在 Postfix 2.2 版本中,仅 smtp(8) 客户端支持访问此缓存。
当启用 SMTP 连接缓存(见下一节)时,smtp(8) 客户端在邮件传输完成后不会断开连接,而是将连接交由 scache(8) 服务器,该服务器会保持连接打开一段时间。
在将打开的连接交由scache(8)服务器后,smtp(8)客户端将继续处理其他邮件投递请求。同时,任何smtp(8)客户端进程都可以向scache(8)服务器请求该缓存连接并重复使用它进行邮件投递。
/-- smtp(8) --> Internet qmgr(8) |
|
|
|
v\-- smtp(8) ^
|scache(8)
使用 TLS 连接重用(Postfix 3.4 及更高版本),Postfix smtp(8) 客户端连接到远程 SMTP 服务器并发送明文 EHLO 和 STARTTLS 命令,然后将 tlsproxy(8) 进程到连接中,如下所示。
发送邮件后,smtp(8) 客户端将打开的 smtp(8)-to-tlsproxy(8) 连接给 scache(8) 服务器,并继续处理其他邮件投递请求。同时,任何 smtp(8) 客户端进程都可以向 scache(8) 服务器请求该缓存连接并重复使用它进行邮件投递。
/-- smtp(8) --> tlsproxy(8) --> Internet qmgr(8) |
|
|
|
v\-- smtp(8) ^
|scache(8)
连接缓存可通过目标域名(收件人地址的右侧)或连接另一端主机的 IP 地址进行搜索。这使 Postfix 即使在远程主机是不同域名邮件服务器时也能复用连接。
连接缓存配置
Postfix smtp(8) 客户端支持两种连接缓存策略:
按需连接缓存。此功能默认启用,并通过 smtp_connection_cache_on_demand 配置参数控制。当此功能启用时,Postfix smtp(8) 客户端会在目标队列的 活动队列 中邮件量较高时,自动将连接保存到连接缓存中。
示例:
/etc/postfix/main.cf: smtp_connection_cache_on_demand = yes
按目的地缓存连接。通过在 smtp_connection_cache_destinations 配置参数中明确列出特定目的地来启用此功能。在完成向选定目的地发送邮件后,Postfix smtp(8) 客户端 始终 将连接保存到连接缓存中。
指定以逗号或空格分隔的目的地或伪目的地列表:
- 如果邮件未通过 中继主机 发送:一个域名(电子邮件地址的右侧部分,不包括数字 IP 地址周围的 [])
- 如果邮件通过中继主机发送:一个中继主机名称(不包括[]或非默认TCP端口),如在main.cf或传输映射中指定
- 一个包含域名和/或 中继主机 名称的文件名,这些名称如上所述
- 一个 "type:table",其中左侧包含域名和/或 中继主机 名称。右侧结果来自 "type:table" 查找,该部分被忽略
示例:
/etc/postfix/main.cf: smtp_connection_cache_destinations = $relayhost smtp_connection_cache_destinations = hotmail.com, ... smtp_connection_cache_destinations = static:all (不推荐)
参见 客户端 TLS 连接复用 以启用通过 TLS 加密连接进行多次投递(Postfix 3.4 及更高版本)。
连接缓存安全机制
连接缓存必须谨慎使用。长时间保持未使用的 SMTP 连接打开是不礼貌的,通过同一连接发送大量消息也是不明智的。为了避免 SMTP 连接缓存问题,Postfix 实现了以下安全机制:
- Postfix 的 scache(8) 服务器仅在有限时间内保持连接打开。时间限制通过 smtp_connection_cache_time_limit 和 connection_cache_ttl_limit 配置参数指定。这可防止恶意行为。
Postfix 的 smtp(8) 客户端仅在有限次数内重用会话。这避免了在无法正确处理单个会话中多次投递的实现中触发错误。
从 Postfix 2.3 开始,建议使用 smtp_connection_reuse_time_limit 参数限制连接复用。此外,Postfix 2.11 提供了 smtp_connection_reuse_count_limit 限制连接可被重用的次数,但此功能存在安全隐患,因为它会引入"致命吸引子"故障模式(当目标有两个或更多入站 MTA 时,最慢的入站 MTA 会吸引 Postfix 的大部分连接到该目标)。
Postfix 2.3 会记录多次使用的连接使用计数,如下例所示:
Nov 3 16:04:31 myname postfix/smtp[30840]: 19B6B2900FE: to=<[email protected]>, orig_to=<wietse@test>, relay=mail.example.com[1.2.3.4], conn_use=2, delay=0.22, delays=0.04/0.01/0.05/0.1, dsn=2.0.0, status=sent (250 2.0.0 Ok)
- 连接缓存会显式标记每个缓存连接的目标域名和 IP 地址信息。连接缓存查找仅在指定正确信息时成功。这可防止邮件误送。
连接缓存限制
Postfix SMTP 连接缓存与某些应用程序冲突:
- 在 Postfix 版本 <3.4 中,Postfix 共享连接缓存无法与 TLS 一起使用,因为打开的 TLS 连接只能在创建它的进程中重用。因此,Postfix smtp(8) 客户端在通过 TLS 尝试投递邮件后,历史上有关闭连接的习惯。
- Postfix 连接缓存目前不支持每个邮件服务器使用多个 SASL 账户。具体来说,Postfix 连接缓存假设 SASL 凭据对通过同一邮件服务器 IP 地址和 TCP 端口发送邮件的所有主机名或域名均有效,且假设 SASL 凭据不依赖于消息发送者。
连接缓存统计信息
scache(8) 连接缓存服务器记录关于缓存峰值大小和缓存命中率的统计信息。这些信息每 connection_cache_status_update_time 秒记录一次,当进程在最大空闲时间后终止时,或当 Postfix 重新加载时。
- 按域的连接缓存查找命中率将告诉您连接缓存的有用性。
- 按网络地址的连接缓存查找始终会失败,除非您正在向共享相同 MX 主机的不同域发送邮件。
- 当未尝试访问连接缓存时,不会记录任何统计信息。