Postfix/Postfwd结合负载均衡场景下的邮件发送阈值管控问题咨询
先跟你对齐下我理解的场景哈:你们公司用多台Postfix 3.5.8服务器发邮件,现在碰到了Google的发送阈值超标问题,打算用Postfwd来做流量管控,但现有架构是JavaMailer这类功能有限的发送客户端,把邮件发到一个指向负载均衡的别名,再由负载均衡分发给各个Postfix服务器。核心痛点是:如果某台部署了Postfwd的Postfix触发阈值返回421错误,负载均衡根本感知不到这个状态,可能还会把后续邮件转到其他Postfix服务器,最后还是整体超阈值;而且那些功能有限的客户端也没法处理延迟重试的逻辑,对吧?
结合我做邮件运维的经验,给你几个可行的解决方案,你可以根据自身情况挑选:
方案一:让Postfwd用分布式计数共享阈值状态
Postfwd默认是单节点独立计数的,这在负载均衡场景下完全不适用——每台服务器的计数各算各的,根本起不到全局管控的作用。你可以把Postfwd的计数存储改成分布式的,比如用Redis来统一存储所有Postfwd节点的统计数据。这样不管负载均衡把请求分到哪台服务器,Postfwd都能拿到全局的发送计数,一旦触发阈值,所有节点都会返回421,不会出现“一台限制了另一台还在发”的情况。
具体操作的话,你需要在Postfwd的配置里启用Redis支持,把计数规则的存储指向你的Redis实例。举个配置片段的例子:
action=rate(sender_domain/1h/1000/redis:redis://your-redis-host:6379)
这样所有Postfwd节点都会从同一个Redis实例读写计数,实现全局统一的阈值管控。
方案二:给负载均衡配置会话粘性(临时过渡方案)
如果暂时不想改动Postfwd的存储架构,你可以给负载均衡配置基于发件人域名/IP的会话粘性。简单说就是,同一个发件人过来的邮件,始终被分配到同一台Postfix+Postfwd服务器上。这样当这台服务器触发阈值返回421后,客户端(如果能处理重试的话)下次发邮件还是会到这台,就能收到延迟提示。
不过这个方案有明显短板:如果某台服务器故障,会话粘性就会失效;而且如果某个发件人的流量集中在一台服务器,可能单台先触发阈值,其他服务器还处于闲置状态,资源利用率不高;另外那些没法处理重试的客户端,还是会继续发邮件或者丢件。所以这个更适合临时过渡,不建议长期用。
方案三:把Postfwd部署成统一入口(推荐长期方案)
另一种更合理的思路是,不在每台Postfix上部署Postfwd,而是单独部署一组Postfwd服务器作为统一的流量入口,放在负载均衡的前面。所有客户端的邮件先发给Postfwd集群,Postfwd在这里做全局的阈值管控,只有通过检查的邮件才会被转发到后面的负载均衡,再分发给Postfix服务器发送。
这个方案的好处是管控逻辑集中,不用操心后面Postfix集群的状态,而且可以给Postfwd做集群化高可用(比如用Keepalived或者负载均衡自身来管理Postfwd节点)。不过需要调整你的邮件路由配置,让客户端指向新的Postfwd入口地址。
额外小建议:优化客户端的重试逻辑
你提到有些客户端(比如JavaMailer)功能有限,没法处理延迟重试。其实JavaMailer是可以配置重试策略的,比如碰到421错误时,延迟一段时间再重试,而不是立刻重发。你可以调整客户端的配置,比如设置mail.smtp.sendpartial、mail.smtp.timeout,再自定义重试的间隔和次数,这样即使收到421,客户端也会自动延迟发送,不会继续触发阈值。
另外,针对Google的阈值限制,除了Postfwd的管控,你也可以检查下Postfix的smtp_destination_concurrency_limit、smtp_destination_rate_delay这些参数,做一些全局的速率限制,和Postfwd的规则配合起来,效果会更好。
备注:内容来源于stack exchange,提问作者Dub Pankhurst

