Maison  >  Article  >  développement back-end  >  Comment implémenter la limitation de courant du bucket de jetons en PHP

Comment implémenter la limitation de courant du bucket de jetons en PHP

藏色散人
藏色散人original
2021-11-23 10:06:412806parcourir

Comment implémenter la limitation de courant du bucket de jetons en PHP : 1. Configurez un bucket de jetons et stockez les jetons dans le bucket ; 2. Prenez un jeton du bucket pour chaque visite 3. Selon la situation réelle, vous pouvez chaque période ; mettez quelques jetons à tout moment ou remplissez directement le seau de jetons.

Comment implémenter la limitation de courant du bucket de jetons en PHP

L'environnement d'exploitation de cet article : système Windows 7, PHP7.1, ordinateur Dell G3.

Comment PHP implémente-t-il la limite actuelle du bucket de jetons ?

php utilise l'algorithme de bucket de jetons pour réaliser un contrôle du trafic basé sur redis

Cet article présente php basé sur redis et utilise l'algorithme de bucket de jetons pour contrôler le trafic d'accès, fournissant une description complète de l'algorithme et un exemple de démonstration, que tout le monde peut apprendre et utiliser facilement.

Chaque fois qu'il y a de longues vacances nationales ou des festivals importants, les sites touristiques nationaux ou les métros seront bondés de monde, ce qui entraînera une charge excessive. Certains adopteront des mesures de restriction de flux pour limiter le nombre de personnes entrant dans la zone. réduit à une certaine valeur, l’entrée sera autorisée.

Par exemple :
Le nombre maximum de personnes autorisées dans la zone est de M
Le nombre actuel de personnes dans la zone est de N
Chaque fois qu'une personne entre, N+1, lorsque N = M, l'entrée n'est pas autorisée
Chaque fois qu'une personne part, N-1, lorsque N < La pression sur le serveur monte soudainement, surchargeant le système.

Bien sûr, nous pouvons ajouter des serveurs pour partager la pression. Tout d'abord, l'ajout de serveurs prend également un certain temps à configurer, et si des serveurs sont ajoutés en raison d'une certaine activité, ces ressources de serveur seront gaspillées une fois l'activité terminée. sur.

Nous pouvons donc d'abord utiliser la

limitation de courant

pour réduire la pression des serveurs en fonction du type d'entreprise. Différent de la limite de circulation dans les sites pittoresques, Le temps entre la visite et la fin du système est très court

, il suffit donc de connaître la durée moyenne de chaque visite et de définir le nombre maximum de visiteurs simultanés.

Algorithme du seau de jetons

1. Tout d'abord, il y a un seau de jetons, et les jetons sont stockés dans le seau. Au début, les jetons dans le seau sont pleins (le nombre de jetons dans le seau peut être défini en fonction). à la situation du serveur).

2. Chaque visite prend un jeton du bucket Lorsque le jeton dans le bucket est 0, plus aucune visite n'est autorisée.

3. De temps en temps, ajoutez des jetons jusqu'à ce que le seau soit plein de jetons. (Vous pouvez mettre plusieurs jetons à intervalles ou remplir directement le seau de jetons en fonction de la situation réelle)

Nous pouvons utiliser la file d'attente de

redis

comme conteneur du seau de jetons, utilisez

lPush (mettre en file d'attente), rPop (retirer la file d'attente) , implémente les opérations d'ajout et de consommation de jetons.
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?>

démo :

<?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());
}

?>
Sortie :

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
Rejoindre régulièrement l'algorithme de jeton

Rejoindre régulièrement le jeton, peut utiliser crontab Réalisé, chaque minute Appeler le méthode add pour ajouter plusieurs jetons.

L'intervalle d'exécution minimum de crontab est de 1 minute. Si les jetons du compartiment de jetons ont été consommés dans les premières secondes, alors les jetons ne seront pas obtenus dans les dizaines de secondes restantes, ce qui entraînera un temps d'attente pour l'utilisateur. plus long.

Nous pouvons optimiser l'algorithme d'ajout de jetons et ajouter plusieurs jetons toutes les quelques secondes en une minute. Cela peut garantir qu'il y a une chance d'obtenir des jetons à chaque fois en une minute.

Le programme d'ajout de jetons appelé par crontab est le suivant, ajoutant automatiquement 3 jetons par seconde.

<?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);
}

?>
Le programme de consommation de simulation est le suivant, consommant 2 à 8 jetons par seconde.

<?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);
}

?>
Démonstration

Définissez une tâche planifiée et exécutez-la une fois par minute

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

Consommation de simulation d'exécution

php consume_demo.php

Résultats d'exécution :

[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

Parce que le seau de jetons est plein au début (nombre maximum de jetons 10) , donc Le jeton peut être obtenu les 10 premières fois. Après la 10ème fois, l'accès sera restreint en fonction du moment où le jeton consommé est supérieur au nombre de jetons ajoutés.

Apprentissage recommandé : "
Tutoriel vidéo PHP

"

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn