一个简易版支付路由实现

一个简易版支付路由实现

By 马冬亮(凝霜  Loki)

一个人的战争(http://blog.csdn.net/MDL13412)

缘起

今天跟小伙伴晚上出去吃饭,聊到:假如用户选着工行支付,总共有1000单,其中300单给财付通,100 单给百付宝,600给支付宝(支付渠道)

算法设计

我们先看一种简单的情况:取一个大小为1000的数组,其中[0-300)分配给财富通,[300,400)分配给百付宝,[400,1000)分配给支付宝(当然可以约分后再计算,这里为了清晰,不进行约分),如下图所示:


对于这个数组,我们将对应区间内的内容填充为相应支付渠道的引用,使用一个计数器,每次有支付请求到来时,就将计数器加1并对1000取余,并用这个计数器去引用数组中的支付渠道,就可以按比例分配的目的。

这个算法的一些弊端:

  • 支付渠道分配不均匀,一段时间内的请求,都会是某个支付渠道;
  • 动态添加、删除支付渠道困难;
  • 动态改变支付渠道所占比例困难;
  • 在支付路由集群中,会造成比例不稳定;

下面我们对这一算法进行改进:

按照支付渠道的数量开一个数组,里面维护一些计算比例的基本信息,如下图所示:

一个简易版支付路由实现_第1张图片

这次,计数器在支付渠道之间自增、取余,而对应的支付渠道,利用hitRate和curHitCount两个变量来模拟上一个算法中区间的比例;

例如,当前有1001次支付请求:

第1次,选择财付通渠道,对应的财付通渠道curHitCount加1,其值小于对应的hitRate,因此可以选择;

第2次,选择百付宝渠道,符合条件,选取;

第3次,选择支付宝渠道,符合条件,选取;

第4次,选择财付通渠道,符合条件,选取;

第5次,选择百付宝渠道,符合条件,选取;

......

第300次,选择支付宝渠道,符合条件,选取;

第301次,选择百付宝渠道,因为此时其curHitCount = 100,大于等于其hitRate,按照其权重,其不再进行分配,选择下一渠道支付宝,符合条件,选取;

第302次,因为上次百付宝选取失败,选择了支付宝,因此这次计数器取余后的结果为0,因此选取财付通渠道,符合条件,选取;

第303次,选择百付宝渠道,同样因为权重问题,跳过,选择下一渠道支付宝,符合条件,选取;

....

在700次请求后,财付通渠道的权重用完,因此后续的300次支付全部使用支付宝渠道;

第1001次,因为所有支付渠道权重全部用完,因此需要重置个系统的curHitCount,开始新一轮的分配。

实现

为了让算法更清晰,去掉了各种校验以及多线程的处理,让读者更容易理解。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

class BankRouter
{
    typedef map            BankHistRateIndexMapping_t;
    struct BankHitRateCounter
    {
        BankHitRateCounter(const string name, const unsigned int rate) :
            hitRate(rate),
            curHitCount(0),
            disabled(false),
            bankName(name)
        {
        }
        unsigned int    hitRate;
        unsigned int    curHitCount;
        bool            disabled;
        const string    bankName;
    };
    typedef vector  BankRateArray_t;

public:
    typedef pair  BankHitRate_t;

public:
    BankRouter(list bankHitRate)
    {
        int index = 0;
        for (list::const_iterator iter = bankHitRate.begin();
                bankHitRate.end() != iter; ++iter) {
            // 去重、校验等逻辑省略...
            bankRateArray_.push_back(
                    BankHitRateCounter(iter->first, iter->second));
            bankRateMapping_[iter->first] = index++;
        }
        bankRateLength_ = bankRateArray_.size();
    }
    virtual ~BankRouter()
    {
    }

    string nextBank()
    {
        if (curBankPtr_ >= bankRateLength_) {
            curBankPtr_ = 0;
        }
        int curBankPtrBk = curBankPtr_;
        bool flag = false;
        while (true) {
            if (curBankPtr_ >= bankRateLength_) {
                curBankPtr_ = 0;
                flag = true;
            }
            // 如果遍历一遍还没有合适的银行可供选择,则说明所有银行均不可用
            // 或者计数器已满,需要重置
            if (curBankPtrBk == curBankPtr_ && flag) {
                // 检测是否全部为disabled,防止死循环,略...
                resetBankRateArray();
                curBankPtr_ = 0;
                curBankPtrBk = 0;
                flag = false;
            }
            if (bankRateArray_[curBankPtr_].disabled) {
                ++curBankPtr_;
            } else {
                if (++bankRateArray_[curBankPtr_].curHitCount <=
                        bankRateArray_[curBankPtr_].hitRate) {
                    return bankRateArray_[curBankPtr_++].bankName;
                } else {
                    ++curBankPtr_;
                }
            }
        }
    }

    void enableBank(string bankName)
    {
        bankRateArray_[bankRateMapping_[bankName]].disabled = false;
    }

    void dienableBank(string bankName)
    {
        bankRateArray_[bankRateMapping_[bankName]].disabled = true;
    }

private:
    void resetBankRateArray()
    {
        for (BankRateArray_t::iterator iter = bankRateArray_.begin();
                bankRateArray_.end() != iter; ++iter) {
            iter->curHitCount = 0;
        }
    }

private:
    BankRouter(const BankRouter &);
    BankRouter &operator =(const BankRouter &);


private:
    BankRateArray_t                 bankRateArray_;
    int                             curBankPtr_;
    int                             bankRateLength_;
    BankHistRateIndexMapping_t      bankRateMapping_;
};

void test(BankRouter &br, const int n)
{
    for (int i = 0; i < n; ++i) {
        cout << br.nextBank() << endl;
    }
}


int main(int argc, const char *argv[])
{
    list l;
    l.push_back(make_pair("cft", 3));
    l.push_back(make_pair("bfb", 1));
    l.push_back(make_pair("zfb", 6));

    BankRouter br(l);

    test(br, 20);
    cout << "--------------------" << endl;

    br.dienableBank("cft");
    test(br, 14);
    cout << "--------------------" << endl;

    br.enableBank("cft");
    test(br, 20);

    return 0;
}

运行结果(大家可以观察支付渠道的切换):

cft
bfb
zfb
cft
zfb
cft
zfb
zfb
zfb
zfb
cft
bfb
zfb
cft
zfb
cft
zfb
zfb
zfb
zfb
--------------------
bfb
zfb
zfb
zfb
zfb
zfb
zfb
bfb
zfb
zfb
zfb
zfb
zfb
zfb
--------------------
cft
cft
cft
cft
bfb
zfb
cft
zfb
cft
zfb
zfb
zfb
zfb
cft
bfb
zfb
cft
zfb
cft
zfb

你可能感兴趣的:(C/C++,人在Qunar)