qshape
postfix提供了一个叫qshape的工具,用于诊断postfix的队列问题,比如队列突然变大等。工具命令的示例如下:
$ qshape -s hold | head T 5 10 20 40 80 160 320 640 1280 1280+ TOTAL 486 0 0 1 0 0 2 4 20 40 419 yahoo.com 14 0 0 1 0 0 0 0 1 0 12 extremepricecuts.net 13 0 0 0 0 0 0 0 2 0 11 ms35.hinet.net 12 0 0 0 0 0 0 0 0 1 11 winnersdaily.net 12 0 0 0 0 0 0 0 2 0 10 hotmail.com 11 0 0 0 0 0 0 0 0 1 10 worldnet.fr 6 0 0 0 0 0 0 0 0 0 6 ms41.hinet.net 6 0 0 0 0 0 0 0 0 0 6 osn.de 5 0 0 0 0 0 1 0 0 0 4
上图显示的是hold队列中排名前10位的发送者所属域名的分布情况,时间单位为分钟,-s表示按sender的域名进行分组排序,默认展示的是按收件域名分组的分布情况,如果不加队列参数则默认展示incoming和active队列的信息。
同时查看多个队列的消息可以用下面的方式:
$ qshape incoming active deferred
通过这个工具,可以分析出从哪些域名发出了大量的邮件,或者哪些域名接收了大量邮件,分别集中在哪个时间段等等信息。知道了哪个域名发送或接收到的邮件量比较大,接下来就可以通过邮件收发日志来查询更具体的情况了。
在正常情况下,incoming和active队列都会保持比较空的状态,也就是邮件一进来就会马上被发送走。
针对未验证域名的字典攻击会导致延迟队列(deferred)的堵塞:如果没有正确验证有不合法域名的收件人会导致邮件投递不成功,从而被反弹到延迟队列,使其产生堆积。不过,如果incoming和active队列能保持比较空的状态,延迟队列稍有增长一般来说不会导致很大的危害。
如果有大量收件人所在的服务器很慢或者干脆挂了,也很容易导致延迟队列堵塞,严重时还会导致active队列也堵塞,这时最好尽量让邮件进入延迟队列或者干脆让发件人稍后再发送邮件。
anvil(8) 是postfix用来防止ddos攻击的服务,可以对进信频率进行限制,但要小心慎用,限制也不要设置得太小!
另外,外发投递时不要把smtp并发数量设置得过大,容易导致对方拒绝,这样延迟队列又堵塞了,如果能重用smtp连接就更佳。
下面介绍postfix里的各种队列
maildrop队列:通过sendmail指令发送的邮件,在还未被pickup进程加入到postfi主队列中时会放在maildrop队列,即使postfix没有启动,邮件也可以加入这个队列,postfix一启动就会开始处理这个队列。
所有进入postfix主队列的邮件都会经过cleanup服务,cleanup负责信封和信头的重写、信头和邮件内容的正则检查、对邮件暗送地址进行处理、由milter对内容进行处理,然后将邮件送入incoming队列。
hold队列:管理员可以通过smtpd进程的一些策略或者cleanup信头和内容检查的一些规则让邮件自动转移到hold队列,邮件将一直呆在这个队列直到管理员再次调度这些邮件。postsuper指令可以手工将hold队列中的邮件转移到deferred队列。hold队列中的邮件可以存活得比$maximal_queue_lifetime. 配置的时间更长,这时如果想释放这些邮件可以使用postsuper -r将其转移到maildrop队列让其重新发送,这样就会给邮件标记一个新的时间戳,而那些比较年轻的邮件可以使用postsuper -H转移到deferred队列。
incoming队列:进入postfix的邮件将会由cleanup服务写入到incoming队列,新的队列文件的拥用者会被设置为"postfix",权限被设置为0600,当一个队列文件准备好可以进行下一步处理时cleanup服务会将文件权限设置为0700并通知队列管理器有新邮件到达,而队列管理器也会忽略掉那些0600的文件。队列管理器会扫描incoming队列将新邮件转移到active队列直到active队列满了(deferred也是如此处理)。一般来说,incoming队列都会比较空,因为邮件一到达就会被转移到active,如果incoming的进信速度超过active的接收速度就会堵塞。影响队列管理速度的因素主要是磁盘io和针对trivial-rewrite service的lookup查询,所以尽量不要使用那些慢查询服务如ldap,mysql等。
in_flow_delay 参数用于控制队列管理器的进信速度,当cleanup服务不能从队列管理器中取得token时,它将在创建新的队列文件前暂停in_flow_delay 秒钟,这样可以控制进信速度。
active队列:有点像操作系统的进程队列,该队列中的邮件都是准备要发送的邮件,就像处于runnable中的进程一样。 active队列与其他队列不同,它实际上是处于内存中的一组数据结构的集合,而其他队列是磁盘上的一个目录,并不占用内存。active队列中邮件的信封信息是在内存中,方便队列管理器来做全局的调度,分配投递代理进程。
active队列的邮件来自于incoming和deferred队列,在这个队列中,有多个收件人的邮件会分成不同的组,这些组共享相同的transport/nexthop组合,分组的大小受到传输层并发大小的限制。
由于active队列的邮件是要投递到目标服务器,所以目标服务器缓慢会导致active队列堵塞。如果目标服务器挂掉一段时间,队列管理器会将其标记为dead,并立即将要投往这个服务器的所有邮件转移到延迟队列(deferred)。
注意:当队列管理器重启时,active队列目录中可能还存在一些邮件,但是这时进程内存中却是空的,所以为了能active的内存状态,队列管理器会将邮件转移到incoming队列,利用它的队列扫描器将邮件重新填充到active队列,这个过程是比较消耗资源的,所以尽量少重启队列管理器,比如postfix reload之类。
active队列的大小受到$qmgr_message_active_limit限制,同时也受到收件人数量限制:$qmgr_message_recipient_limit,这两个配置默认都是20000。当队列达到上限时active将不再接受新的邮件。
deferred队列:那些因为暂时的原因投递失败的邮件将会放在该队列,由队列管理器定时扫描这个队列重新投递,定时间隔由参数queue_run_delay配置。由于incoming队列也有个定时扫描器,所以队列管理器会轮流转移这两个列表的邮件到active队列。
这个队列里的邮件有一个冷却时间:在邮件进入延迟队列时会被设置一个未来的时间,这样只有过了这个时间该邮件才会重新进进行投递尝试。冷却时间界于$minimal_backoff_time 和 $maximal_backoff_time之间,每次重试都会把重试时间翻倍然后调整到界限之间,所以比较新的邮件会比老邮件重试得更频繁。