因为现在动不动就说高并发,说到高并发 就不得不提并发下限流、熔断、降级。
为什么要进行接口限流呢?
个人认为其实目的都是为了保证线上系统的稳定性,防止因为高频访问服务器而导致服务器宕机。

下面来简单实现一下接口限流的常用算法:

1.使用计数器进行限流

这应该是最简单也是最容易实现的,比如A接口1分钟内的访问次数不能超过100个。那么可以这么做:在一开始的时候,设置一个计数器counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100并且该请求与第一个请求的间隔时间还在1分钟之内,那么说明请求数过多;如果该请求与第一个请求的间隔时间大于1分钟,就重置计数器。为了保证高并发下的原子性使用redis的incr实现计数器限流。

speedCounterApi.php文件

<?php
//计数器实现限流
class speedCounterApi
{//要访问的接口public function getApi(){$res = (new speedCounterApi())->SpeedCounter();$data = ['msg' => '获取成功','code' => 200];if (!$res) {$data['msg'] = '请稍后重试';$data['code'] = 400;}return $data;}/*** 1.redis 计数器实现方式* 限制1分钟内最大只能请求10次*/public function SpeedCounter(){$redis = new \Redis();$redis->connect('10.225.137.105', 6379);$limitTime = 60;// 最大请求数量$maxCount = 10;$redisKey = 'api';// incr命令// 为键 key 储存的数字值加上一。// 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。$count = $redis->incr($redisKey);print_r($count);echo PHP_EOL;//设置计算器的时间if ($count == 1) {//设置 Key -/s的过期时间$redis->expire($redisKey, $limitTime);}//1分钟内超过最大请求数if ($count > $maxCount) {return false;}return true;}
}$res = (new speedCounterApi())->getApi();print_r($res);echo PHP_EOL;

然后来执行一下查看效果


手动执行php文件 前面10次都可以访问后面10次我们主动拦截请求 粗略的看是实行了限流的方法。这方法存在的问题就是最后1秒内涌入所有请求,然后计数器过期重置后第一秒内又涌入大量请求 这样服务器还是可能会被高频访问搞挂。为了解决这种方法又出现了滑动窗口算法

2.滑动窗口算法


百度拿的图,滑动窗口个人认为其实就是多存了时间,每次请求进来后时间范围之外的数据将被动态删除。主要使用redis的zset结构来实现

slideTimeWindow.php文件

<?php//2.滑动窗口实现
class SlideTimeWindowApi
{//要访问的接口public function getApi(){$res = (new SlideTimeWindowApi())->SlideTimeWindow();$data = ['msg' => '获取成功','code' => 200];if (!$res) {$data['msg'] = '请稍后重试';$data['code'] = 400;}return $data;}/*** 2.redis 滑动窗口实现方式* 限制1分钟内最大只能请求10次* 使用redis事务保证redis原子性*/public function SlideTimeWindow(){$redis = new \Redis();$redis->connect('10.225.137.105', 6379);$limitTime = 60;// 最大请求数量$maxCount = 10;$redisKey = 'slide_api';$nowTime = time();//使用管道提升性能$pipe = $redis->multi();//value 和 score 都使用时间戳,因为相同的元素会覆盖$sss=$pipe->zadd($redisKey, $nowTime, $nowTime);//移除时间窗口之前的行为记录,剩下的都是时间窗口内的$pipe->zremrangebyscore($redisKey, 0, $nowTime - $limitTime);//获取窗口内的行为数量$pipe->zcard($redisKey);//多加一秒过期时间$pipe->expire($redisKey, 60 + 1);//执行$replies = $pipe->exec();var_dump($replies[2]);//判断在有限时间窗口内的数量是否超过限制 true:falsereturn $replies[2] <= $maxCount;}
}
$res = (new SlideTimeWindowApi())->getApi();
print_r($res);
echo PHP_EOL;

主要就是根据时间判断总体思路和计数器差不多
然后来执行一下查看效果


可以看到10次之后就拦截了请求。

3.漏斗算法

顾名思义漏斗,其实就是处理的速度是一定的。主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏斗算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。但是多余的请求将会被直接丢弃。
我更觉得是将流量控制在自己可以承受范围内,自己控制流量的速度。
处理请求的worker以固定的速度从桶中取出请求进行处理。
如果桶满了,直接返回请求频率超限的错误码或者页面
限流 体现在worker从桶中取请求的速度上

流量最均衡的限流实现方式。nginx的limit模块就是使用了漏斗算法

leackBucketApi.php文件

<?php/*** 3.漏斗算法实现方式*/
class leackBucketApi
{private $_water;    //漏斗的当前水量(也就是请求数)private $_burst=2;    //漏斗总量(超过将直接舍弃)private $_rate = 1; //漏斗出水速率(限流速度)private $_lastTime; //记录每次请求的时间(因为需要记录每次请求之间的出水量也就是请求数)private $_redis;    //redis对象public function __construct(){$this->_redis = new \Redis();$this->_redis->connect('10.225.137.105', 6379);$this->_lastTime = time(); //需要记录每次访问的时间}//要访问的接口public function getApi(){$res = (new leackBucketApi())->leackBucket();$data = ['msg' => '获取成功','code' => 200];if (!$res) {$data['msg'] = '请稍后重试';$data['code'] = 400;}return $data;}/****/public function leackBucket(){$now = time();$redisKey = 'leackBucket_api';if (!empty($time = $this->_redis->get($redisKey))) {$this->_lastTime = $time; //获取上一次访问时间}if (!empty($water = $this->_redis->get('water'))) {$this->_water = $water;//获取当前剩余量也就是请求数}//计算出水量//因为rate是固定的,所以可以认为“时间间隔 * rate”即为漏出的水量$s=$now - $this->_lastTime; //当前时间减去上次访问时间,得到时间间隔$outCount=$s * $this->_rate; //漏出的水量(请求数)//执行漏水,计算剩余水量,也就是当前请求$this->_water=($this->_water - $outCount);if($this->_water<0){$this->_water=0; //重置为0}var_dump($this->_water.'----请求数');//请求数超出了突发请求限制if($this->_water > $this->_burst){echo "超出桶限制".PHP_EOL;return  false;}$this->_lastTime = $now; // 更新时间$water++;//更新待请求数$this->_redis->set($redisKey,$now);//记录请求数$this->_redis->set('water',$water);return true;}
}$demo=new leackBucketApi();//模拟请求
while (true){$time=mt_rand(600000,3600000);echo "'执行请求结果:".PHP_EOL;var_dump($demo->getApi());var_dump('随机时间:'.$time);usleep($time); //随机时间
}

然后来执行一下查看效果

不足之处在于:
面对突发流量时会有大量请求失败,但是我们可以使用预先准备好的资源返回。

4.令牌桶算法


令牌桶算法在漏桶算法流量处理均衡的基础上,允许一定程度的突发流量。
适合电商抢购或者微博出现热点事件这种场景。
令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌。
假设限制r/s,表示每秒会有r个令牌放入桶中,或者说每过1/r秒桶中增加一个令牌 桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝 当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上 如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么在缓冲区等待)

对于令牌桶中令牌的产生一般有两种做法:
1、开启一个定时任务,由定时任务持续生成令牌。这样的问题在于会极大的消耗系统资源,如,某接口需要分别对每个用户做访问频率限制,假设系统中 存在6W用户,则至多需要开启6W个定时任务来维持每个桶中的令牌数,这样的开销是巨大的。
2、在每次获取令牌之前计算,其实现思路为,根据时间来计算访问前后该段时间内可以生成多少令牌,将生成的令牌加入令牌桶中并 更新数据。这样一来,只需要在获取令牌时计算一次即可。

令牌桶的主要限流体现在放入令牌的速度。
tokenBucketApi.php文件

<?php/*** 4.令牌桶算法实现*/class tokenBucketApi
{private $_lastTime;     //记录每次请求的时间private $_burst = 2;    // 桶的容量private $_rate = 1;     // 令牌放入的速度private $_tokens;       // 当前令牌的数量private $_redis;        //redis对象public function __construct(){$this->_redis = new \Redis();$this->_redis->connect('10.225.137.105', 6379);$this->_lastTime = time(); //需要记录每次访问的时间}//要访问的接口public function getApi(){$res = (new tokenBucketApi())->tokenBucket();$data = ['msg' => '获取成功','code' => 200];if (!$res) {$data['msg'] = '请稍后重试';$data['code'] = 400;}return $data;}public function tokenBucket(){$now = time();$redisKey = 'tokenBucket_api';if (!empty($time = $this->_redis->get($redisKey))) {$this->_lastTime = $time; //获取上一次访问时间}if (!empty($tokens = $this->_redis->get('tokens'))) {$this->_tokens = $tokens;//获取当前令牌的数量}//计算生成的令牌量//因为rate是固定的,所以可以认为“时间间隔 * rate”即为生成的令牌量$s = $now - $this->_lastTime; //当前时间减去上次访问时间,得到时间间隔//生成的令牌 =(当前时间-上次刷新时间)* 放入令牌的速率$addTokens = $s * $this->_rate;//当前令牌数= 之前的桶内令牌数量+放入的令牌数量(不能超过桶的总量)$this->_tokens = min($this->_burst, $this->_tokens + $addTokens);echo "令牌量:".$this->_tokens;//桶里面还有令牌,请求正常处理$this->_lastTime = $now;$this->_redis->set($redisKey, $now);if ($this->_tokens < 1) {// 若不到1个令牌,则拒绝echo "拿不到令牌".PHP_EOL;return false;}// 还有令牌,领取令牌$this->_tokens -= 1;//记录请求数$this->_redis->set('tokens', $this->_tokens);return true;}
}$demo = new tokenBucketApi();
var_dump($demo->getApi());
//模拟请求
//while (true) {//   $time = mt_rand(600000, 3600000);
//  echo "'执行请求结果:" . PHP_EOL;
// var_dump($demo->getApi());
//var_dump('随机时间:' . $time);
//usleep($time); //随机时间
}

然后手动执行多次


可以看到当慢慢执行的话 一直可以获取到令牌 因为令牌桶里一直有令牌,当请求过快将会丢弃(当然最好是返回我们预定好的资源)。过段时间令牌桶里又有了令牌又可以获取到令牌请求api。

放一张他们的对比图。

php接口限流实现方法相关推荐

  1. 亿级流量场景下如何为HTTP接口限流?看完我懂了!!

    这里,我们实现Web接口限流,具体方式为:使用自定义注解封装基于令牌桶限流算法实现接口限流. 不使用注解实现接口限流 搭建项目 这里,我们使用SpringBoot项目来搭建Http接口限流项目,Spr ...

  2. nginx 如何实现读写限流的方法

    这篇文章主要介绍了nginx 如何实现读写限流的方法的相关资料,这里提供实例代码及如何配置,需要的朋友可以参考下 nginx 读写限流 前段时间,开发了一个供外部调用的api,领导说要限流,请求单个I ...

  3. java 接口防刷_java轻量级接口限流/防刷插件

    简介 call-limit提供接口限流.防刷的功能,插件基于spring开发,在应用应用的任何一个逻辑层皆可使用(web.service.dao), 插件支持单机应用下的限流和分布式应用的限流(分布式 ...

  4. java guava限流,Guava的RateLimiter实现接口限流

    最近开发需求中有需要对后台接口进行限流处理,整理了一下基本使用方法. 首先添加guava依赖: com.google.guava guava 23.0 然后封装RateLimiter适用对多接口的限制 ...

  5. redis rua解决库存问题_库存秒杀问题-redis解决方案- 接口限流

    /** * Created by PhpStorm. * redis 销量超卖秒杀解决方案 * redis 文档:http://doc.redisfans.com/ * ab -n 10000 -c ...

  6. Java接口限流算法

    0. 前言 常见的限流算法有:令牌桶.漏桶.计数器也可以进行粗暴限流实现. 1. 算法介绍 1.1 令牌桶算法 令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌.令牌桶算法的描述如下: ...

  7. 接口限流算法及解决方案

    参考: 接口限流算法:漏桶算法&令牌桶算法 文章目录 一.限流算法 1. 漏桶算法 2. 令牌桶算法 二.令牌桶算法VS漏桶算法 三.解决方案 1. 使用Guava的RateLimiter进行 ...

  8. Redis 做接口限流

    Redis 除了做缓存,还能干很多很多事情:分布式锁.限流.处理请求接口幂等性...太多太多了- 今天想和小伙伴们聊聊用 Redis 处理接口限流,这也是最近的 TienChin 项目涉及到这个知识点 ...

  9. 使用令牌桶算法解决调用第三方接口限流问题

    我们在调用第三方接口时常常会碰到接口限流问题,为了解决这一问题,大家想出了许多方法.我这里介绍一下我的方法,第三方接口限流一般是基于令牌桶算法的,那么我们可以以彼之道还治彼身,使用令牌桶算法实现我方调 ...

最新文章

  1. docker本地仓库镜像
  2. 字节跳动面试题:用归并排序判断冒泡排序的轮数
  3. python编程300例_经典编程100例——python版(例9例10)
  4. codevs 1779 单词的划分
  5. 重学TCP协议(4) 三次握手
  6. linux测试手柄,Linux Joystick 介绍
  7. 春节抽空读了8本书,只有这本书我1字不落的读完了!(上)
  8. 20 个强大的 Sublime Text 插件
  9. 我给女朋友讲编程html系列(1) -- Html快速入门
  10. killall 引起应用程序挂起【原创】
  11. PyTorch 1.7 发布:支持 CUDA 11、FFT 新 API、及 Windows 分布式训练
  12. LeetCode33. 搜索旋转排序数组(二分查找)
  13. 桌面壁纸的deepin或者uos水印如何去除
  14. 零基础学习SQL Server(一)---查询语句在项目实例中的执行
  15. sqlite3数据库-常用命令
  16. excel中输入身份证号后三位变成0怎么办
  17. 违章查询功能如何实现
  18. win32com处理excel数据透视表格式
  19. Python数据可视化之散点图和折线图
  20. 【一】MADDPG-单智能体|多智能体总结(理论、算法)

热门文章

  1. 实战:基于 RocketMQ 的电商高并发秒杀场景
  2. 第65天:仿网易轮播图
  3. 打卡小程序搭建教程分享
  4. 如何下载微软虚拟学院MVA字幕
  5. GitHub 与各代码托管平台比较
  6. 一对一视频app开发选择如何合适算法
  7. Modbus网关调试(卓岚5143D+Modbus Slave)
  8. Aardio做的一个小说在线阅读器
  9. 共勉之--少年壮志不言愁(施一公为清华大学2015级研究生新生的演讲)
  10. 《我来说说DO-178B标准》