日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
PHP如何實(shí)現(xiàn)令牌桶限流

本文操作環(huán)境:Windows7系統(tǒng)、php7.1、Dell G3電腦。

PHP如何實(shí)現(xiàn)令牌桶限流?

php 基于redis使用令牌桶算法實(shí)現(xiàn)流量控制

本文介紹php基于redis,使用令牌桶算法,實(shí)現(xiàn)訪問流量的控制,提供完整算法說明及演示實(shí)例,方便大家學(xué)習(xí)使用。

每當(dāng)國內(nèi)長假期或重要節(jié)日時(shí),國內(nèi)的景區(qū)或地鐵都會(huì)人山人海,導(dǎo)致負(fù)載過大,部分則會(huì)采用限流措施,限制進(jìn)入的人數(shù),當(dāng)區(qū)內(nèi)人數(shù)降低到一定值,再允許進(jìn)入。

例如:
區(qū)內(nèi)最大允許人數(shù)為 M
區(qū)內(nèi)當(dāng)前人數(shù)為 N
每進(jìn)入一個(gè)人,N+1,當(dāng)N = M時(shí),則不允許進(jìn)入
每離開一個(gè)人,N-1,當(dāng)N < M時(shí),可允許進(jìn)入

系統(tǒng)在運(yùn)行過程中,如遇上某些活動(dòng),訪問的人數(shù)會(huì)在一瞬間內(nèi)爆增,導(dǎo)致服務(wù)器瞬間壓力飆升,使系統(tǒng)超負(fù)荷工作。

當(dāng)然我們可以增加服務(wù)器去分擔(dān)壓力,首先增加服務(wù)器也需要一定的時(shí)間去配置,而且因?yàn)槟骋粋€(gè)活動(dòng)而增加服務(wù)器,活動(dòng)結(jié)束后這些服務(wù)器資源就浪費(fèi)了。

因此我們可以根據(jù)業(yè)務(wù)類型,先使用限流的方式去減輕服務(wù)器壓力。

與景區(qū)限流不同,系統(tǒng)的訪問到結(jié)束的時(shí)間非常短,因此我們只需要知道每個(gè)訪問持續(xù)的平均時(shí)間,設(shè)定最多同時(shí)訪問的人數(shù)即可。

令牌桶算法

1.首先設(shè)有一個(gè)令牌桶,桶內(nèi)存放令牌,一開始令牌桶內(nèi)的令牌是滿的(桶內(nèi)令牌的數(shù)量可根據(jù)服務(wù)器情況設(shè)定)。

2.每次訪問從桶內(nèi)取走一個(gè)令牌,當(dāng)桶內(nèi)令牌為0,則不允許再訪問。

3.每隔一段時(shí)間,再放入令牌,最多使桶內(nèi)令牌滿額。(可以根據(jù)實(shí)際情況,每隔一段時(shí)間放入若干個(gè)令牌,或直接補(bǔ)滿令牌桶)

我們可以使用redis的隊(duì)列作為令牌桶容器使用,使用lPush(入隊(duì)),rPop(出隊(duì)),實(shí)現(xiàn)令牌加入與消耗的操作。

TrafficShaper.class.php

_config = $config;        $this->_queue = $queue;        $this->_max = $max;        $this->_redis = $this->connect();
    }    /**
     * 加入令牌
     * @param  Int $num 加入的令牌數(shù)量
     * @return Int 加入的數(shù)量
     */
    public function add($num=0){

        // 當(dāng)前剩余令牌數(shù)
        $curnum = intval($this->_redis->lSize($this->_queue));        // 最大令牌數(shù)
        $maxnum = intval($this->_max);        // 計(jì)算最大可加入的令牌數(shù)量,不能超過最大令牌數(shù)
        $num = $maxnum>=$curnum+$num? $num : $maxnum-$curnum;        // 加入令牌
        if($num>0){            $token = array_fill(0, $num, 1);            $this->_redis->lPush($this->_queue, ...$token);            return $num;
        }        return 0;

    }    /**
     * 獲取令牌
     * @return Boolean
     */
    public function get(){
        return $this->_redis->rPop($this->_queue)? true : false;
    }    /**
     * 重設(shè)令牌桶,填滿令牌
     */
    public function reset(){
        $this->_redis->delete($this->_queue);        $this->add($this->_max);
    }    /**
     * 創(chuàng)建redis連接
     * @return Link
     */
    private function connect(){
        try{            $redis = new Redis();            $redis->connect($this->_config['host'],$this->_config['port'],$this->_config['timeout'],$this->_config['reserved'],$this->_config['retry_interval']);            if(empty($this->_config['auth'])){                $redis->auth($this->_config['auth']);
            }            $redis->select($this->_config['index']);
        }catch(RedisException $e){            throw new Exception($e->getMessage());            return false;
        }        return $redis;
    }


} // class end?>

demo:

 'localhost',
    'port' => 6379,
    'index' => 0,
    'auth' => '',
    'timeout' => 1,
    'reserved' => NULL,
    'retry_interval' => 100,
);

// 令牌桶容器
$queue = 'mycontainer';

// 最大令牌數(shù)
$max = 5;

// 創(chuàng)建TrafficShaper對象
$oTrafficShaper = new TrafficShaper($config, $queue, $max);

// 重設(shè)令牌桶,填滿令牌
$oTrafficShaper->reset();

// 循環(huán)獲取令牌,令牌桶內(nèi)只有5個(gè)令牌,因此最后3次獲取失敗
for($i=0; $i<8; $i++){
    var_dump($oTrafficShaper->get());
}

// 加入10個(gè)令牌,最大令牌為5,因此只能加入5個(gè)
$add_num = $oTrafficShaper->add(10);

var_dump($add_num);

// 循環(huán)獲取令牌,令牌桶內(nèi)只有5個(gè)令牌,因此最后1次獲取失敗
for($i=0; $i<6; $i++){
    var_dump($oTrafficShaper->get());
}

?>

輸出:

boolean true
boolean true
boolean true
boolean true
boolean true
boolean false
boolean false
boolean false
int 5
boolean true
boolean true
boolean true
boolean true
boolean true
boolean false

定期加入令牌算法

定期加入令牌,我們可以使用crontab實(shí)現(xiàn),每分鐘調(diào)用add方法加入若干令牌。

crontab最小的執(zhí)行間隔為1分鐘,如果令牌桶內(nèi)的令牌在前幾秒就已經(jīng)被消耗完,那么剩下的幾十秒時(shí)間內(nèi),都獲取不到令牌,導(dǎo)致用戶等待時(shí)間較長。

我們可以優(yōu)化加入令牌的算法,改為一分鐘內(nèi)每若干秒加入若干令牌,這樣可以保證一分鐘內(nèi)每段時(shí)間都有機(jī)會(huì)能獲取到令牌。

crontab調(diào)用的加入令牌程序如下,每秒自動(dòng)加入3個(gè)令牌。

 'localhost',
    'port' => 6379,
    'index' => 0,
    'auth' => '',
    'timeout' => 1,
    'reserved' => NULL,
    'retry_interval' => 100,
);

// 令牌桶容器
$queue = 'mycontainer';

// 最大令牌數(shù)
$max = 10;

// 每次時(shí)間間隔加入的令牌數(shù)
$token_num = 3;

// 時(shí)間間隔,最好是能被60整除的數(shù),保證覆蓋每一分鐘內(nèi)所有的時(shí)間
$time_step = 1;

// 執(zhí)行次數(shù)
$exec_num = (int)(60/$time_step);

// 創(chuàng)建TrafficShaper對象
$oTrafficShaper = new TrafficShaper($config, $queue, $max);

for($i=0; $i<$exec_num; $i++){
    $add_num = $oTrafficShaper->add($token_num);
    echo '['.date('Y-m-d H:i:s').'] add token num:'.$add_num.PHP_EOL;
    sleep($time_step);
}

?>

模擬消耗程序如下,每秒消耗2-8個(gè)令牌。

 'localhost',
    'port' => 6379,
    'index' => 0,
    'auth' => '',
    'timeout' => 1,
    'reserved' => NULL,
    'retry_interval' => 100,
);

// 令牌桶容器
$queue = 'mycontainer';

// 最大令牌數(shù)
$max = 10;

// 每次時(shí)間間隔隨機(jī)消耗的令牌數(shù)量范圍
$consume_token_range = array(2, 8);

// 時(shí)間間隔
$time_step = 1;

// 創(chuàng)建TrafficShaper對象
$oTrafficShaper = new TrafficShaper($config, $queue, $max);

// 重設(shè)令牌桶,填滿令牌
$oTrafficShaper->reset();

// 執(zhí)行令牌消耗
while(true){
    $consume_num = mt_rand($consume_token_range[0], $consume_token_range[1]);
    for($i=0; $i<$consume_num; $i++){
        $status = $oTrafficShaper->get();
        echo '['.date('Y-m-d H:i:s').'] consume token:'.($status? 'true' : 'false').PHP_EOL;
    }
    sleep($time_step);
}

?>

演示

設(shè)置定時(shí)任務(wù),每分鐘執(zhí)行一次

* * * * * php /程序的路徑/cron_add.php >> /tmp/cron_add.log

執(zhí)行模擬消耗

php consume_demo.php

執(zhí)行結(jié)果:

[2018-02-23 11:42:57] consume token:true
[2018-02-23 11:42:57] consume token:true
[2018-02-23 11:42:57] consume token:true
[2018-02-23 11:42:57] consume token:true
[2018-02-23 11:42:57] consume token:true
[2018-02-23 11:42:57] consume token:true
[2018-02-23 11:42:57] consume token:true
[2018-02-23 11:42:58] consume token:true
[2018-02-23 11:42:58] consume token:true
[2018-02-23 11:42:58] consume token:true
[2018-02-23 11:42:58] consume token:true
[2018-02-23 11:42:58] consume token:true
[2018-02-23 11:42:58] consume token:true
[2018-02-23 11:42:58] consume token:false
[2018-02-23 11:42:59] consume token:true
[2018-02-23 11:42:59] consume token:true
[2018-02-23 11:42:59] consume token:true
[2018-02-23 11:42:59] consume token:false
[2018-02-23 11:42:59] consume token:false
[2018-02-23 11:42:59] consume token:false
[2018-02-23 11:42:59] consume token:false
[2018-02-23 11:43:00] consume token:true
[2018-02-23 11:43:00] consume token:true
[2018-02-23 11:43:00] consume token:true
[2018-02-23 11:43:00] consume token:false
[2018-02-23 11:43:00] consume token:false

因令牌桶一開始是滿的(最大令牌數(shù)10),所以之前的10次都能獲取到令牌,10次之后則會(huì)根據(jù)消耗的令牌大于加入令牌數(shù)時(shí),限制訪問。


當(dāng)前標(biāo)題:PHP如何實(shí)現(xiàn)令牌桶限流
文章出自:http://www.5511xx.com/article/djpedjg.html