邮件系统-Postfix2.5并发访问反馈算法

邮件系统会面临这么一种场景:
    当邮件需要被递送到下一个邮件网关时,我们的邮件网关将会尝试连接下一个MTA传递邮件。他可以一次发一封邮件,也可以一次发多封邮件。这里出现一个均衡问题,每次发的少,MTA完全可以接收更多的邮件,那么将会降低有效的递送率,相反如果一次发送太多消息,将会增加下一个MTA的负担,导致连接失败的可能性。那么怎么设定这种发送策略呢,让我们看看POSTFIX是怎么解决这个问题。

以下大部分内容来源于POSTFIX官方文档
http://www.postfix.org/SCHEDULER_README.html


1.现存并发调度方法的缺陷
  
  最开始,postfix使用一种很简单但却很健壮的发送方法,每当尝试连接发送但失败后,会减少1个并发数,反之增加一个并发数。当然并发数不能超过配置参数maximum per-destination.当并发数降低到0时,我们认为目标主机处于死亡状态,发送终止。

+/-1并发反馈算法的缺陷
(1)倍数增长的并发连接数将会导致高并发问题的通道。比如,我们默认的初始化并发参数为5,那么并发连接数将会出现5-10-20的增长,这样将会使连接状况过热。
(2)同理,连续多次的失败将会过快地降低并发数,而使目标主机被标识为“死亡”态。

改进的并发调度方法将会有效的解决这一问题。
它将并发连接控制和死亡探测分成两种手段。
它使用“less-than-1”反馈机制来提供更加渐进的并发连接适应性,同时它使用反馈滞后机制以减少并发连接率的浮动。。
同时,区别于等待并发连接降至0来表示主机死亡的方法,目标主机将被判定为死亡在多次连接失败后,这个失败次数由配置决定。


现在来看看POSTFIX2.5提供的并发反馈算法

当一定数量的发送任务顺利完成后,我们可以增长并发连接数以提高消息的发送效率(没必要连续)。我们通过正向反馈率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)时,并发连接数将降低到较低的水平。


小结:postfix2.5的并发反馈算法包含可以概括为
(1)可选择的less-than-1正向反馈机制能够避免服务器过热
(2)可选择的less-than-1负向反馈机制能够避免投递过早的放弃。
(3)反馈延迟(feedback hysteresis)能后避免快速的网络浮动


POSTFIX2.5的"死亡主机"探测算法

当我们递送邮件遇到部分递送连接超时时,我们会试图停止递送。老的调度算法在负向反馈使并发连接降到0时标识主机为死亡状态。使用less-than-1算法会是这个过程过于缓慢。所以我们将“死亡探测”方法独立出来。post-fix2.5算法将在N次负向并发反馈后标识主机为死亡状态。(N可配置)


POSTFIX 2.5的并发调度算法的伪代码


Pseudocode for the Postfix 2.5 concurrency scheduler

The pseudo code shows how the ideas behind new concurrency scheduler are implemented as of November 2007. The actual code can be found in the module qmgr/qmgr_queue.c.

Types:
        Each destination has one set of the following variables
        int concurrency
        double success
        double failure
        double fail_cohorts

Feedback functions:
        N is concurrency; x, y are arbitrary numbers in [0..1] inclusive
        positive feedback: g(N) = x/N | x/sqrt(N) | x
        negative feedback: f(N) = y/N | y/sqrt(N) | y

Initialization:
        concurrency = initial_concurrency
        success = 0
        failure = 0
        fail_cohorts = 0

After success:
        fail_cohorts = 0
        Be prepared for feedback > hysteresis, or rounding error
        success += g(concurrency)
        while (success >= 1)            Hysteresis 1
            concurrency += 1            Hysteresis 1
            failure = 0
            success -= 1                Hysteresis 1
        Be prepared for overshoot
        if (concurrency > concurrency limit)
            concurrency = concurrency limit

Safety:
        Don't apply positive feedback unless
            concurrency < busy_refcount + init_dest_concurrency
        otherwise negative feedback effect could be delayed

After failure:
        if (concurrency > 0)
            fail_cohorts += 1.0 / concurrency
            if (fail_cohorts > cohort_failure_limit)
                concurrency = 0
        if (concurrency > 0)
            Be prepared for feedback > hysteresis, rounding errors
            failure -= f(concurrency)
            while (failure < 0)
                concurrency -= 1        Hysteresis 1
                failure += 1            Hysteresis 1
                success = 0
            Be prepared for overshoot
            if (concurrency < 1)
                concurrency = 1

你可能感兴趣的:(算法,F#)