使用Easyswoole 开发项目有一段时间了,官方的ip 限流方法比较简陋,我这里自己手动实现了一套基于令牌桶算法限流的方案,这里记录分享下

实现功能

1:根据ip 限速
2:可以配置缓冲池,防止突发流量
3:可以配置黑白名单,或者及时调整指定ip的流量

实现流程

一:创建IpAccess 类

<?php
/*** Created by PhpStorm.* User: 05* Date: 2020/8/18* Time: 10:14** ip限流模块*/namespace App\Model;use EasySwoole\Component\TableManager;
use Swoole\Exception;
use Swoole\Table;class IpAccess
{/** @var IpAccess */private static  $install=null;protected $table;protected $burst=0;protected $rate=0;protected $ips=[];private function __construct(int $burs,int $rate,$ips){$this->burst=$burs;$this->rate=$rate;$this->ips=$ips;//初始化一个保存最大50w条ip 的内存表TableManager::getInstance()->add('ipList', ['ip' => ['type' => Table::TYPE_STRING,'size' => 16],'burst' => ['type' => Table::TYPE_INT,'size' => 8],'rate' => ['type' => Table::TYPE_INT,'size' => 6],'rest' => ['type' => Table::TYPE_INT,'size' => 8],'lastAccessTime' => ['type' => Table::TYPE_INT,'size' => 8]], 1024 * 512);$this->table = TableManager::getInstance()->get('ipList');}/*** @param array ['rate'=>10,'burst'=>100,'ips'=>['127.0.0.1'=>['rate'=>10,'burst'=>100],'xxx.xxx.xx.x'=>['rate'=>10,'burst'=>100]]]* @return IpAccess|Table* @throws Exception*/public static function regist(array $conf=[]) {if (empty(self::$install)){$rate=50;$burst=200;$ips=[];if (isset($conf['rate'])){$rate=$conf['rate'];}if (isset($conf['burst'])){$burst=$conf['burst'];}if (isset($conf['ips'])){$ips=$conf['ips'];}self::$install=new static($burst,$rate,$ips);}return self::$install;}public static function getInstall() :IpAccess {return self::$install;}/*** 手动添加ip信息,及时黑名单或者调整白名单流速* @param array $ips ['127.0.0.1'=>['rate'=>10,'burst'=>100],'xxx.xxx.xx.x'=>['rate'=>10,'burst'=>100]]*/public function addIps(array $ips){if (!empty($ips)){$this->ips=array_merge($this->ips,$ips);foreach ($ips as $key=> $v) {$tkey = substr(md5($key), 8, 16);//删除key 下次重新创建$this->table->del($tkey);}return true;}return false;}/*** @param string $ip 客户端ip* @return int false 超速,int =剩余次数*/public function access(string $ip): int{$key = substr(md5($ip), 8, 16);$info = $this->table->get($key);$tempBurst=$this->burst;$tempRate=$this->rate;$tempIps=$this->ips;if (in_array($ip,$tempIps)){$tempBurst=$tempIps['burst'];$tempRate=$tempIps['rate'];}if ($info) {$nowRest=$info['rest']-1;//判断是否超流if ($nowRest<0){return false;}$this->table->set($key, ['lastAccessTime' => time(),'rest' => $nowRest,]);return $nowRest;} else {$this->table->set($key, ['ip' => $ip,'lastAccessTime' => time(),'burst' => $tempBurst,'rate' => $tempRate,'rest' => $tempBurst+$tempRate]);return $tempBurst+$tempRate-1;}}//定时增加令牌,外部勿主动调用public function tick($token=''){if ($token!=='IpAccessTask'){return false;}foreach ($this->table as $key => $item) {//如果没用到缓冲池 则直接删除ip,等待下次累计,如果已经用到缓冲池 则往池子里增加if($item['rest']<$item['burst']){$rest=$item['rest']+$item['rate'];$this->table->set($key, ['rest' => $rest,]);}else{$this->table->del($key);}}}//删除所有的public function clear(){foreach ($this->table as $key => $item) {$this->table->del($key);}}/*** 访问记录* @param int $count 5s内访问频率大于这个数据的* @return array*/public function accessList($count = 10): array{$ret = [];foreach ($this->table as $key => $item) {if ($item['rest'] < $item['burst'] +$item['rate'] -$count) {$ret[] = $item;}}return $ret;}}

二:创建定时任务添加令牌类

<?php
/*** Created by PhpStorm.* User: 05* Date: 2020/8/18* Time: 17:05*/
namespace App\Model;
use EasySwoole\Component\Process\AbstractProcess;
use EasySwoole\Component\TableManager;
use EasySwoole\EasySwoole\Config;
use EasySwoole\Session\Session;
use EasySwoole\Task\AbstractInterface\TaskInterface;
use Swoole\Coroutine;
use Swoole\Exception;
use Swoole\Table;class IpAccessTask implements TaskInterface
{function onException(\Throwable $throwable, int $taskId, int $workerIndex){// TODO: Implement onException() method.}function run(int $taskId, int $workerIndex){try {IpAccess::getInstall()->tick('IpAccessTask');} catch (Exception $e) {}}
}

三:启动server 时 在 EasySwooleEven->mainServerCreate 注册限流,并启动令牌桶任务

  //启动限流任务,这里先初始化创建内存表,不要再onrequest里创建,不然进程不共享try {//每个ip 5s 内最多访问50次,缓冲池500,本机随便访问$ipConf=['rate'=>50,'burst'=>300,'ips'=>['127.0.0.1'=>['rate'=>5000,'burst'=>10000]]];IpAccess::regist($ipConf);} catch (Exception $e) {var_dump($e->getMessage());}//这里每5s 检测一遍令牌,可以自己根据需求定义,时间太频繁注意性能Timer::tick(5*1000,function (){$task =TaskManager::getInstance();$task->async(new IpAccessTask());});

4:在 EasySwooleEven->onRequest 里 进行限流判断

//判断限流$ipAccess=IpAccess::getInstall();$head= $request->getHeaders();$realIp=$head['x-real_ip'][0];//超出速度流量if (!$ipAccess->access($realIp)){//可以进行适当提醒,发邮件啥的$response->write("server is busy,plase try later");return false;}

三:性能测试
这里测试了添加10w 个ip 跟检测10w 个ip 所需的时间
环境 腾讯云 1核 2g 主机
添加10w 个IP 大概耗时 400 ms
检测10w 个IP 大概耗时 178ms

Easyswoole 令牌桶IP限流实现(企业实用)相关推荐

  1. 单个接口添加超时时间_SpringBoot使用Guava令牌桶对接口限流

    pom.xml引入jar包 com.google.guava guava 29.0-jre 代码 @RestControllerpublic class HelloController { //创建令 ...

  2. 信号量与令牌桶_限流的4种方式令牌桶实战

    限流的4种方式 正文 限流 限流是对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机.常用的限流算法有令牌桶和和漏桶,而Google开源项目Guava ...

  3. java同名过滤器_Gateway Redis令牌桶请求限流过滤器

    spring cloud gateway默认基于redis令牌桶算法进行微服务的限流保护,采用RateLimter限流算法来实现. 1.引入依赖包 org.springframework.cloud ...

  4. nginx根据ip限流和突发流量配置解释

    前言 前一篇记录了如何使用Nginx代理Vue项目,今天记录如果使用Nginx配置location限流,本篇是Nginx专栏第5篇, 有想学习nginx的可以订阅下该专栏,大家一起讨论,有问题可以留言 ...

  5. Nginx(OpenResty)+Lua+Redis IP限流 10s内

    使用 OpenResty 可以不用再次编译nginx 就能集成对应lua环境 可以扩展的模块比较丰富 1.使用redis 控制限流 ip 访问频度 创建对应lua脚本 access_by_limit_ ...

  6. 限流10万QPS、跨域、过滤器、令牌桶算法-网关Gateway内容都在这儿

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 作者:雄 ...

  7. 高可用系统设计 | 分布式限流策略:计数器算法、漏桶算法、令牌桶算法

    文章目录 限流 什么是限流? 分布式限流 限流算法 计数器算法 固定窗口计数器 滑动窗口计数器 漏桶算法 令牌桶算法 限流 什么是限流? 限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已 ...

  8. 【229期】Spring Boot 使用令牌桶算法+拦截器+自定义注解+自定义异常实现简单的限流...

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每天 08:15 更新文章,每天进步一点点... ...

  9. 基于Redis和 Lua 实现分布式令牌桶限流

    rpc-tech-stack 系列的实践文章 ~ 本文属于限流话题. 限流是一个很大的话题,准备把其中的所有限流器都实现一遍,以此也算全都写过了,到时候再用也不至于会心虚,毕竟真实写完成过.本文主要讲 ...

最新文章

  1. java this context,java – Spring XML中applicationcontext的“this”引用
  2. java怎么不安装到c盘的教程,帮您设置win10系统安装不了java的详细步骤
  3. SDUT-3378_数据结构实验之查找六:顺序查找
  4. javaweb mysql 连接池 c3p0 配置_JavaWeb基础—数据库连接池DBCP、C3P0
  5. myBatis xml if、where、if-else?、foreach 心得
  6. Spring JDBC最佳实践(2)
  7. poj 3469(网络流模版)
  8. 腾讯优图刷新人体姿态估计国际权威榜单,相关论文被ECCV2020收录
  9. 【英语学习】【WOTD】hoopla 释义/词源/示例
  10. 事业编还是程序员_34岁程序员月薪3万2,跳槽被国企录取,看到月收入后却犹豫了!...
  11. UI设计灵感|如何将数据摆放得整整齐齐?
  12. 18、HTML5 Video(视频)和 audio(音频)
  13. unity的函数生命周期
  14. 【Software】动软代码生成器
  15. java 字符转换为字符串_java中将字符(Char)转换为字符串的四种方式
  16. 摄动法在计算机中的应用,摄动法及其在电力系统中的应用
  17. 语音学习笔记3------matlab实现傅里叶反变换ifft()函数
  18. python 模拟汽车转动
  19. 程序员日常工作英文记录
  20. 对于System.out.println简单了解

热门文章

  1. [转载]linux grep 多个关键字用法
  2. typora主题更改(以及旧版本下载地址)
  3. 使用Clumsy和Process Explorer定位软件高CPU占用问题
  4. 如何使用py2exe,以及解决ImportError: No module named py2exe问题
  5. 学习c语言还是web前端好,是该学UI设计还是Web前端开发呢?
  6. 如果有一天我不再烦你了
  7. 使用SDWebImage清除图片缓存
  8. Angular实现递归指令 - Tree View
  9. pic单片机c语言哪一种好,PIC单片机挑选哪一个比较好?国产替代方案有哪些?...
  10. minicom指令_linux minicom 自动化对串口下命令