이 기사에서는 Redis 기반의 PHP를 소개하고 토큰 버킷 알고리즘을 사용하여 액세스 트래픽을 제어하며 누구나 배우고 사용할 수 있도록 완전한 알고리즘 설명과 데모 예제를 제공합니다.
국내 명절이나 중요한 축제가 있을 때마다 국내 명승지나 지하철은 사람들로 붐비기 때문에 일부 사람들은 해당 지역의 인원이 줄어들 때 흐름 제한 조치를 취합니다. 특정 값 이상으로 진입이 허용됩니다.
예:
지역에 허용되는 최대 인원은 M입니다.
현재 지역의 인원은 N입니다.
한 사람이 들어올 때마다 N+1, N일 때 = M, 출입금지
사람이 나갈 때마다 N-1, N < 순간적인 압력 스파이크로 시스템에 과부하가 걸립니다.
물론 서버를 추가하여 부담을 나눌 수도 있습니다. 우선, 서버를 추가하는 것도 구성하는 데 일정 시간이 걸리고 특정 활동으로 인해 서버를 추가하는 경우 활동이 끝난 후 이러한 서버 리소스가 낭비됩니다. 위에.
그러므로 먼저
현재 제한을 사용하여 비즈니스 유형에 따른 서버 부담을 줄일 수 있습니다. 명승지의 교통 제한과 다르게,
방문부터 시스템 종료까지의 시간이 매우 짧기 때문에각 방문의 평균 지속 시간만 알고 최대 동시 방문자 수를 설정하면 됩니다. 토큰 버킷 알고리즘
2. 각 방문은 버킷에서 토큰을 가져옵니다. 버킷의 토큰이 0이면 더 이상 방문이 허용되지 않습니다.
3. 가끔씩 버킷이 토큰으로 가득 찰 때까지 토큰을 추가하세요. (실제 상황에 따라 여러 개의 토큰을 일정한 간격으로 넣거나 토큰 버킷을 직접 채울 수 있습니다)
토큰 버킷 컨테이너로
redis의 큐를 사용할 수 있으며, lPush(enqueue), rPop( dequeue), 토큰 추가 및 소비 작업을 구현합니다. less , 매분 add 메서드를 호출하여 여러 토큰을 추가합니다. crontab 사용에 대해서는 "Linux crontab 예약 작업 실행 명령 형식 및 자세한 예"를 참조하세요. crontab의 최소 실행 간격은 1분입니다. 토큰 버킷에 있는 토큰이 처음 몇 초 내에 소모된 경우 그러면 남은 수십초 안에 토큰을 얻을 수 없어 오랜 시간 동안 기다려야 합니다.
우리는 토큰 추가 알고리즘을 최적화하고 1분 내에 몇 초마다 여러 토큰을 추가할 수 있습니다. 이를 통해 1분 내에 매번 토큰을 얻을 수 있는 기회가 보장됩니다. crontab에서 호출하는 토큰 조인 프로그램은 다음과 같으며, 초당 3개의 토큰을 자동으로 추가합니다.
<?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?>
시뮬레이션 소비 프로그램은 다음과 같으며, 초당 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 = 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());
}?>
예약된 작업을 설정하고 1분에 한 번씩 실행
boolean trueboolean trueboolean trueboolean trueboolean trueboolean falseboolean falseboolean falseint 5boolean trueboolean trueboolean trueboolean trueboolean trueboolean false
<?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); }?>
실행 결과:
<?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); }?>
처음에 토큰 버킷이 가득 차 있기 때문입니다(최대 토큰 수 10). 이므로 처음 10회부터 토큰을 획득할 수 있으며, 10회 이후에는 추가된 토큰 수보다 소모된 토큰이 많은 경우에 따라 접근이 제한됩니다.
이 글에서는 PHP가 토큰 버킷 알고리즘을 사용하여 Redis 기반 트래픽 제어를 구현하는 방법을 설명합니다. 더 많은 관련 내용을 보려면 PHP 중국어 웹사이트를 참고하세요.
관련 권장 사항:
Redis 마스터-슬레이브 동기화, 읽기-쓰기 분리 설정 관련 작업
소개 MySQL로의 유도 테이블 파티션을 재구축하는 방법과 데이터 유지
PHP는 고유 RequestID 클래스의 관련 콘텐츠를 생성합니다
위 내용은 PHP는 토큰 버킷 알고리즘을 사용하여 Redis 기반 흐름 제어를 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!