ホームページ  >  記事  >  バックエンド開発  >  PHPでトークンバケットの電流制限を実装する方法

PHPでトークンバケットの電流制限を実装する方法

藏色散人
藏色散人オリジナル
2021-11-23 10:06:412852ブラウズ

トークン バケットの電流制限を実装する PHP メソッド: 1. トークン バケットを設定し、そのバケットにトークンを保存します; 2. 訪問ごとにバケットからトークンを取得します; 3. 実際の状況に従って、時々いくつかのトークンを入れるか、トークンバケットを直接満たしてください。

PHPでトークンバケットの電流制限を実装する方法

#この記事の動作環境: Windows7 システム、PHP7.1、Dell G3 コンピューター。

PHP はトークン バケットの電流制限をどのように実装しますか?

##php はトークン バケット アルゴリズムを使用して、redis に基づいたフロー制御を実装します

##この記事では、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 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 を使用して実装し、add メソッドを呼び出して毎分いくつかのトークンを追加します。

crontab の最小実行間隔は 1 分です。最初の数秒でトークン バケット内のトークンが消費されると、残りの数十秒でトークンが取得されなくなります。これにより、ユーザーはもっと待ってください。

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

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

<?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 = 10;

// 每次时间间隔加入的令牌数
$token_num = 3;

// 时间间隔,最好是能被60整除的数,保证覆盖每一分钟内所有的时间
$time_step = 1;

// 执行次数
$exec_num = (int)(60/$time_step);

// 创建TrafficShaper对象
$oTrafficShaper = new TrafficShaper($config, $queue, $max);

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

?>

シミュレーション消費プログラムは次のとおりで、1 秒あたり 2 ~ 8 個のトークンを消費します。

<?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 = 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; $i<$consume_num; $i++){
        $status = $oTrafficShaper->get();
        echo &#39;[&#39;.date(&#39;Y-m-d H:i:s&#39;).&#39;] consume token:&#39;.($status? &#39;true&#39; : &#39;false&#39;).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 ビデオ チュートリアル


以上がPHPでトークンバケットの電流制限を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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