PHP實作令牌桶限流的方法:1、設有一個令牌桶,桶內存放令牌;2、每次存取從桶內取走一個令牌;3、根據實際情況,每隔一段時間放入若干個令牌或直接補滿令牌桶即可。
本文操作環境:Windows7系統、PHP7.1、Dell G3電腦。
PHP如何實現令牌桶限流?
php 基於redis使用令牌桶演算法實現流量控制
本文介紹php基於redis,使用令牌桶演算法,實現存取流量的控制,提供完整演算法說明及示範實例,方便大家學習使用。
每當國內長假期或重要節日時,國內的景區或地鐵都會人山人海,導致負載過大,部分則會採用限流措施,限制進入的人數,當區內人數降低到一定值,再允許進入。
例如:
區內最大允許人數為M
區內目前人數為N
每進入一個人,N 1,當N = M時,則不允許進入
每離開一個人,N-1,當 N 時,可允許進入
系統在運作過程中,如遇上某些活動,造訪的人數會在一瞬間內爆增,導致伺服器瞬間壓力飆升,使系統超負荷工作。
當然我們可以增加伺服器去分擔壓力,先增加伺服器也需要一定的時間去配置,而且因為某一個活動而增加伺服器,活動結束後這些伺服器資源就浪費了。
因此我們可以根據業務類型,先使用限流的方式去減輕伺服器壓力。
與景區限流不同,系統的訪問到結束的時間非常短,因此我們只需要知道每個訪問持續的平均時間,設定最多同時訪問的人數即可。
令牌桶演算法
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['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:
<?php /** * 演示令牌加入与消耗 */ require 'TrafficShaper.class.php'; // redis连接设定 $config = array( 'host' => 'localhost', 'port' => 6379, 'index' => 0, 'auth' => '', 'timeout' => 1, 'reserved' => NULL, 'retry_interval' => 100, ); // 令牌桶容器 $queue = 'mycontainer'; // 最大令牌数 $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分鐘,如果令牌桶內的令牌在前幾秒就已經被消耗完,那麼剩下的幾十秒時間內,都取得不到令牌,導致用戶等待時間較長。
我們可以優化加入令牌的演算法,改為一分鐘內每若干秒加入若干令牌,這樣可以保證一分鐘內每段時間都有機會能取得到令牌。
crontab呼叫的加入令牌程式如下,每秒自動加入3個令牌。
<?php /** * 定时任务加入令牌 */ require 'TrafficShaper.class.php'; // redis连接设定 $config = array( 'host' => '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; $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個令牌。
<?php /** * 模拟用户访问消耗令牌,每段时间间隔消耗若干令牌 */ require 'TrafficShaper.class.php'; // redis连接设定 $config = array( 'host' => '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; $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); } ?>
示範
設定定時任務,每分鐘執行一次
* * * * * 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中文網其他相關文章!

本文比較了酸和基本數據庫模型,詳細介紹了它們的特徵和適當的用例。酸優先確定數據完整性和一致性,適合財務和電子商務應用程序,而基礎則側重於可用性和

本文討論了確保PHP文件上傳的確保,以防止諸如代碼注入之類的漏洞。它專注於文件類型驗證,安全存儲和錯誤處理以增強應用程序安全性。

本文討論了在PHP中實施API速率限制的策略,包括諸如令牌桶和漏水桶等算法,以及使用Symfony/Rate-limimiter之類的庫。它還涵蓋監視,動態調整速率限制和手

本文討論了使用password_hash和pyspasswify在PHP中使用密碼的好處。主要論點是,這些功能通過自動鹽,強大的哈希算法和SECH來增強密碼保護

本文討論了OWASP在PHP和緩解策略中的十大漏洞。關鍵問題包括注射,驗證損壞和XSS,並提供用於監視和保護PHP應用程序的推薦工具。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)