队列调度
1队列有调度方式:
并发调度和抢占式调度,信用机制。
2现有的并发队列调度的缺点:
最开始,postfix使用一种很简单但却很健壮的发送方法,每当尝试连接发送但失败后,会减少1个并发数,反之增加一个并发数。当然并发数不能超过配置参数maximum per-destination.当并发数降低到0时,我们认为目标主机处于死亡状态,发送终止。
+/-1并发反馈算法的缺陷
(1)倍数增长的并发连接数将会导致高并发问题的通道。比如,我们默认的初始化并发参数为5,那么并发连接数将会出现5-10-20的增长,这样将会使连接状况过热。
(2)同理,连续多次的失败将会过快地降低并发数,而使目标主机被标识为“死亡”态。
改进的并发调度方法将会有效的解决这一问题。它将并发连接控制和死亡探测分成两种手段。 它使用“less-than-1”反馈机制来提供更加渐进的并发连接适应性,同时它使用反馈滞后机制以减少并发连接率的浮动。。
同时,区别于等待并发连接降至0来表示主机死亡的方法,目标主机将被判定为死亡在多次连接失败后,这个失败次数由配置决定。
当一定数量的发送任务顺利完成后,我们可以增长并发连接数以提高消息的发送效率(没必要连续)。我们通过正向反馈率g(N)来实现,其中N为预期的目标主机并发连接数目。每次递送使用g(N)=1的反馈机制后,并发连接将会在每次发送成功后递增,这个跟老的反馈机制相同。当使用g(N)=1/N后,并发连接将会在N次成功的连接反馈后+1。less-than-1反馈机制和取整方法很自然的提供给我们应对网络浮动的缓冲方法。当1/g(N)次正向反馈机制后,并发连接数将达到一个较高的水平。
同理,我们使用负向刺激f(N)=1/N来描述温和的并发退却机制,当并发连接反馈失败达到1/f(n)时,并发连接数将降低到较低的水平。
3小结:postfix2.5的并发反馈算法包含可以概括为
(1)可选择的less-than-1正向反馈机制能够避免服务器过热
(2)可选择的less-than-1负向反馈机制能够避免投递过早的放弃。
(3)反馈延迟(feedback hysteresis)能后避免快速的网络浮动
4队列调度机制所有算法如下:
leaky bucket:通过限定邮件随指数性增长的数量,防止qmgr模块在高负载下发生用尽内存等问题。qmgr_message_active_limit default=20000。
fairness:交替从incoming和deferred队列取信到active队列发送,公平对待新老邮件。
slow start:MDA向收件服务器的发信是并行的,初始并发数由initial_destination_concurrency参数设定,默认值为5。慢启动算法逐步的增加并行值,防止对对方服务器造成过大压力,直到对方拒收或超过default_destination_concurrency_limit(默认20)。
round borin:采用轮询算法公平调度各MDA和各域的邮件发送。
exponentialbackoff:初次投递失败的邮件被送到deferred队列,每次投递失败,双倍递增下次投递时间。
destination status cache:对于无法发送的地址,postfix维护一个列表,在一定时间内不往列表中的目的地发信,以消除没有必要的尝试发信操作。
preemptivemessage scheduling:在发信时可以选择抢占式算法,优先发送等待时间更久更容易发出的信件发送。
fairness、round borin、preemptive message scheduling策略的目的都是为了平衡。fairness策略避免deferred队列中的邮件等待太久。round borin策略优先选择有待发信件的QMGR_TRANSPORT(MDA)和QMGR_QUEEN(收信域)。preemptive message scheduling策略优先选择等待时间更久且更易发送的信件发送,以免部分收信人等待时间过长。
5:相关结构信息:
QMGR_MESSAGE:记录一封邮件的信息。
RESOLVE_REPLY:记录trivial_rewrite模块对邮件地址进行解析的结果。
QMGR_TRANSPORT:表示一类MDA。
QMGR_QUEUE:表示一个收件域。
QMGR_ENTRY:用于做实际的发信操作。
struct QMGR_QUEUE {
int dflags; /* delivery requestoptions */
time_t last_done; /* last deliverycompletion */
char *name; /* domain name oraddress */
char *nexthop; /* domain name */
int todo_refcount; /* queue entries (todo list) */
int busy_refcount; /* queue entries (busy list) */
int window; /* slow open algorithm */
double success; /* accumulated positivefeedback */
double failure; /* accumulated negativefeedback */
double fail_cohorts; /* pseudo-cohort failure count */
QMGR_TRANSPORT *transport; /*transport linkage */
QMGR_ENTRY_LIST todo; /*todo queue entries */
QMGR_ENTRY_LIST busy; /*messages on the wire */
QMGR_QUEUE_LIST peers; /*neighbor queues */
DSN *dsn; /* why unavailable*/
time_t clog_time_to_warn; /* time of last warning */
int blocker_tag; /* tagged if blocks job list */
};
window:被初始化为QMGR_TRANSPORT的init_dest_concurrency字段值。为调度的邮件并发数,初始值默认值为5,随时间的变化而变化,根据之前的算法设定。
一个QMGR_TRANSPORT包含多个QMGR_QUEUE ,一个QMGR_QUEUE包含多个
Qmgr_entry.
QMGR_JOB 使一封信使用不同的MDA发送邮件信息,结构体中保存QMGR_TRANSPORT 的成员变量.
qmgr_job_create(message,transport);
qmgr_active_drain:从active队列发送邮件。
qmgr_active_feed:从incoming队列或deferred队列交替取信到active队列,实现了fairness策略。
qmgr_transport_select:要发信,首先选定要使用的MDA。
qmgr_transport_alloc:使用event_enable_read安排发信事件qmgr_deliver。
qmgr_message_alloc:围绕单封邮件构建内部数据结构。
qmgr_message_read:从active队列(文件系统)读取邮件内容。
qmgr_message_reslove:解析收件人地址及MDA、发信域等信息。
qmgr_message_assign:查找或创建QMGR_JOB、QMGR_PEER、QMGR_ENTRY等结构体。
一封信可能经过多个MDA去传递。所以要使用到QMGR_JOB。QMGR_TRANSPORT利用queue_byname 和 job_byname 查找相应的任务和邮件队列。
6:根据官方文档记载之间的关系:
Each transport maintains a set of queues (describing the destinations it shall talk to) and jobs (referencing the messages it shall deliver).
Each queue belongs to one transport, so each destination may be referred to by several queues, one for each transport.
Each queue maintains a list of all recipient entries (batches of message recipients) which shall be delivered to given destination (the todo list), and a list of recipient entries already being delivered by the delivery agents (the busy list).
Each transport job groups everything it takes to deliver one message via its transport. Each job represents one message within the context of the transport. The job belongs to one transport and message, so each message may have multiple jobs, one for each transport. The job groups all the peer structures, which describe the destinations the job‘s message has to be delivered to.
简单的数据结构图如下:
Qmgr 划分的模块:
qmgr_active_drain() allocates one delivery process.
/* Process allocation is asynchronous. Once the delivery
/* process is available, an attempt is made to deliver
/* a message via it. Message delivery is asynchronous, too.
/*
QMGR_TRANSPORT *qmgr_transport_select(void)
/* qmgr_transport_throttle - disable delivery process allocation */
qmgr_deliver_concurrency is a global counter that says how
/*many delivery processes are in use. This can be used, for
/*example, to control the size of the `active‘ message queue*/
Each queue corresponds to multiple peer structures. Each peer structure is like the queue structure, belonging to one transport and referencing one destination. The difference is that it lists only the recipient entries which all originate from the same message, unlike the queue structure, whose entries may originate from various messages. For messages with few recipients, there is usually just one recipient entry for each destination, resulting in one recipient entry per peer. But for large mailing list messages the recipients may need to be split to multiple recipient entries, in which case the peer structure may list many entries for single destination.
The job groups all the peer structures, which describe the destinations the job‘s message has to be delivered to.