ホームページ  >  記事  >  バックエンド開発  >  PHP はトークン バケット アルゴリズムを使用して、Redis に基づくフロー制御を実装します。

PHP はトークン バケット アルゴリズムを使用して、Redis に基づくフロー制御を実装します。

jacklove
jackloveオリジナル
2018-06-15 17:22:533743ブラウズ

この記事では、redis に基づく PHP を紹介し、トークン バケット アルゴリズムを使用してアクセス トラフィックを制御します。誰でも学習して使用できる、アルゴリズムの完全な説明とデモンストレーションの例を提供します。
国内の長期休暇や重要な祭りがある場合、国内の景勝地や地下鉄は混雑し、入場者数を制限するための流入制限措置が講じられる場合があります。エリア内の人数が一定値まで減少すると、再び立ち入ることができなくなります。

例:
エリア内で許可される最大人数は MM
エリア内の現在の人数は ですN
すべてのエントリ N = M の場合、1 人、N 1 はエントリできません
人が退出するたびに、N -1の場合、N
もちろん、サーバーを追加して圧力を分散することもできます。まず、サーバーの追加には構成にもある程度の時間がかかります。また、特定のアクティビティのためにサーバーが追加された場合、これらのサーバー リソースが無駄になります。活動が終わった後。

したがって、最初に

current Limiting を使用して、ビジネスの種類に応じてサーバーの負荷を軽減できます。 景勝地の交通制限とは異なり、

訪問からシステムの終了までの時間は非常に短いため、各訪問の平均時間を把握し、最大時間を設定するだけで済みます。同時訪問者数。

トークンバケットアルゴリズム

1. まず、トークンバケットがあり、トークンはバケットに格納されます。最初は、トークンバケット内のトークンはいっぱいです。バケット内の数量はサーバーの状況に応じて設定できます)。

2. 各訪問はバケットからトークンを取得します。バケット内のトークンが 0 の場合、それ以上の訪問は許可されません。

3. 時々、バケットがトークンでいっぱいになるまでトークンを追加します。 (実際の状況に応じて、間隔をあけて複数のトークンを投入することも、トークン バケットを直接埋めることもできます)

redis

のキューをトークン バケット コンテナとして使用できます。

lPush (エンキュー)、rPop (デキュー) を使用して、トークンの追加および消費操作を実装します。
TrafficShaper.class.php

<?php/**
 * PHP基于Redis使用令牌桶算法实现流量控制
 * Date:    2018-02-23
 * Author:  fdipzone
 * Version: 1.0
 *
 * Descripton:
 * php基于Redis使用令牌桶算法实现流量控制,使用redis的队列作为令牌桶容器,入队(lPush)出队(rPop)作为令牌的加入与消耗操作。
 *
 * Func:
 * public  add     加入令牌
 * public  get     获取令牌
 * public  reset   重设令牌桶
 * private connect 创建redis连接
 */class TrafficShaper{ // class start
    private $_config; // redis设定
    private $_redis;  // redis对象
    private $_queue;  // 令牌桶
    private $_max;    // 最大令牌数
    /**
     * 初始化
     * @param Array $config redis连接设定
     */
    public function __construct($config, $queue, $max){
        $this->_config = $config;        $this->_queue = $queue;        $this->_max = $max;        $this->_redis = $this->connect();
    }    /**
     * 加入令牌
     * @param  Int $num 加入的令牌数量
     * @return Int 加入的数量
     */
    public function add($num=0){
        // 当前剩余令牌数
        $curnum = intval($this->_redis->lSize($this->_queue));        // 最大令牌数
        $maxnum = intval($this->_max);        // 计算最大可加入的令牌数量,不能超过最大令牌数
        $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;
    }    /**
     * 重设令牌桶,填满令牌
     */
    public function reset(){
        $this->_redis->delete($this->_queue);        $this->add($this->_max);
    }    /**
     * 创建redis连接
     * @return Link
     */
    private function connect(){
        try{            $redis = new Redis();            $redis->connect($this->_config[&#39;host&#39;],$this->_config[&#39;port&#39;],$this->_config[&#39;timeout&#39;],$this->_config[&#39;reserved&#39;],$this->_config[&#39;retry_interval&#39;]);            if(empty($this->_config[&#39;auth&#39;])){                $redis->auth($this->_config[&#39;auth&#39;]);
            }            $redis->select($this->_config[&#39;index&#39;]);
        }catch(RedisException $e){            throw new Exception($e->getMessage());            return false;
        }        return $redis;
    }
} // class end?>

デモ:

<?php/**
 * 演示令牌加入与消耗
 */require &#39;TrafficShaper.class.php&#39;;// redis连接设定$config = array(    &#39;host&#39; => &#39;localhost&#39;,    &#39;port&#39; => 6379,    &#39;index&#39; => 0,    &#39;auth&#39; => &#39;&#39;,    &#39;timeout&#39; => 1,    &#39;reserved&#39; => NULL,    &#39;retry_interval&#39; => 100,
);// 令牌桶容器$queue = &#39;mycontainer&#39;;// 最大令牌数$max = 5;// 创建TrafficShaper对象$oTrafficShaper = new TrafficShaper($config, $queue, $max);// 重设令牌桶,填满令牌$oTrafficShaper->reset();// 循环获取令牌,令牌桶内只有5个令牌,因此最后3次获取失败for($i=0; $i<8; $i++){
    var_dump($oTrafficShaper->get());
}// 加入10个令牌,最大令牌为5,因此只能加入5个$add_num = $oTrafficShaper->add(10);
var_dump($add_num);// 循环获取令牌,令牌桶内只有5个令牌,因此最后1次获取失败for($i=0; $i<6; $i++){
    var_dump($oTrafficShaper->get());
}?>
出力:

boolean trueboolean trueboolean trueboolean trueboolean trueboolean falseboolean falseboolean falseint 5boolean trueboolean trueboolean trueboolean trueboolean trueboolean false
定期的にトークンを追加するアルゴリズム

定期的にトークンを追加するには、crontab を使用して実装し、add メソッドを呼び出して毎分いくつかのトークンを追加します。 crontab の使用方法については、「Linux crontab スケジュールタスク実行コマンドの形式と詳細な例」を参照してください。

トークンバケット内のトークンが消費された場合、crontab の最小実行間隔は 1 分です。最初の数秒間はトークンが取得できず、残りの数十秒間はトークンを取得できず、ユーザーは長時間待たされてしまいます。

トークンを追加するアルゴリズムを最適化し、1 分以内に数秒ごとに複数のトークンを追加することで、1 分以内に毎回トークンを取得できるようになります。

crontab によって呼び出されるトークン追加プログラムは次のとおりで、1 秒あたり 3 つのトークンを自動的に追加します。

f378d75859bf086c646cf8c8a77ac379 'localhost',    'port' => 6379,    'index' => 0,    'auth' => '',    'timeout' => 1,    'reserved' => NULL,    'retry_interval' => 100,
);// 令牌桶容器$queue = 'mycontainer';// 最大令牌数$max = 10;// 每次时间间隔加入的令牌数$token_num = 3;// 时间间隔,最好是能被60整除的数,保证覆盖每一分钟内所有的时间$time_step = 1;// 执行次数$exec_num = (int)(60/$time_step);// 创建TrafficShaper对象$oTrafficShaper = new TrafficShaper($config, $queue, $max);for($i=0; $iac5d8bbec8fb1aae84ade36d7c132775add($token_num);    echo '['.date('Y-m-d H:i:s').'] add token num:'.$add_num.PHP_EOL;
    sleep($time_step);
}?>
シミュレーション消費プログラムは次のとおりで、1 秒あたり 2 ~ 8 個のトークンを消費します。

b0aa8dbae4af6175c5b1872dc35aed72 'localhost',    'port' => 6379,    'index' => 0,    'auth' => '',    'timeout' => 1,    'reserved' => NULL,    'retry_interval' => 100,
);// 令牌桶容器$queue = 'mycontainer';// 最大令牌数$max = 10;// 每次时间间隔随机消耗的令牌数量范围$consume_token_range = array(2, 8);// 时间间隔$time_step = 1;// 创建TrafficShaper对象$oTrafficShaper = new TrafficShaper($config, $queue, $max);// 重设令牌桶,填满令牌$oTrafficShaper->reset();// 执行令牌消耗while(true){    $consume_num = mt_rand($consume_token_range[0], $consume_token_range[1]);    for($i=0; $idd4e44fbcd7068196de2d6c8cad7e133get();        echo '['.date('Y-m-d H:i:s').'] consume token:'.($status? 'true' : 'false').PHP_EOL;
    }
    sleep($time_step);
}?>
デモ

スケジュールされたタスクを設定し、1分ごとに実行します

* * * * * php /程序的路径/cron_add.php >> /tmp/cron_add.log
実行シミュレーションの消費

php consume_demo.php
実行結果:

[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
最初はトークンバケットがいっぱいなので(トークンの最大数は10)、最初の10回まではトークンを取得できますが、10回以降は消費トークンが大きくなります。参加トークンの枚数を超えるとアクセスが制限されます。


この記事では、PHP がトークン バケット アルゴリズムを使用して Redis に基づいたトラフィック制御を実装する方法について説明します。詳細については、PHP の中国語 Web サイトを参照してください。

関連する推奨事項: Redis のマスター/スレーブ同期, 読み書き分離設定の関連操作


#MySQL でテーブルパーティションを再構築する方法と、データを保持します

#

PHP は、一意の RequestID クラスの関連コンテンツを生成します

以上がPHP はトークン バケット アルゴリズムを使用して、Redis に基づくフロー制御を実装します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。