一个基于用户的API限流策略 Rate Limit

1. 限流场景

在开发高并发系统时,有很多种方法可用来保护系统:缓存、降级、限流等。

  • 缓存:提升系统访问速度,增大系统处理能力
  • 降级:服务出现问题或影响核心流程的性能时,需要暂时屏蔽,待高峰过去或问题解决后再打开
  • 限流:部分场景比如:稀缺资源(秒杀,抢购)、写服务(评论、下单)、频繁复杂查询(评论最后几页)等,需要限制这些场景的并发/请求量

限流就是通过对并发访问/请求进行限速或一个时间窗口内的请求进行限速,从而达到保护系统的目的。一般系统可以通过压测来预估能处理的峰值,一旦达到设定的峰值阀值,则可以拒绝服务(定向错误页或告知资源没有了)、排队或等待(例如:秒杀、评论、下单)、降级(返回默认数据)

限流不能乱用,否则正常流量会出现一些奇怪的问题,从而导致用户抱怨。

2. 限流算法

常见限流算法:技术器、令牌桶和漏桶算法。

2.1. 计数器

计数器是最简单粗暴的算法。例如:一个服务每秒能处理100个请求。设置一个1s的滑动窗口,窗口有10个格子,每个格子100ms,每100ms移动一次,每次移动都记录当前服务请求的次数。内存中保存最近10次的次数(LinkedList)。格子每次移动的时候判断一次,最后一个格子和第一个格子的次数是否相差100,如果超过,则进行限流。

当格子划分的越多,那么滑动窗口的滚动越平滑,限流的统计就会越精确。

计数器的实现简单,但是是平均分配1秒钟的请求,然而实际情况中的请求往往是动态的,流量不平滑的。

 

2.2. 令牌桶

令牌桶算法是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌。令牌桶的主要概念如下:

  • 令牌按固定的速率被放入令牌桶中,例如:r tokens/秒
  • 桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝
  • 当一个n字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上
  • 如果桶中的令牌不足n个,则不会删除令牌,且数据包将被限流(丢弃或在缓冲区等待)

令牌桶根据放令牌的速率(r tokens/s)去控制输出的速率(to network)

 

令牌桶的另外一个好处是可以方便的改变速度. 一旦需要提高速率,则按需提高放入桶中的令牌的速率. 一般会定时(比如100毫秒)往桶中增加一定数量的令牌, 有些变种算法则实时的计算应该增加的令牌的数量.

2.3. 漏桶

漏桶( Leaky Bucket )算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率。

漏桶作为计量工具时,可用于流量整形和流量控制,漏桶的主要概念如下:

  • 一个固定容量的漏桶,按照常量固定速率流出水滴(流出请求)
  • 如果桶是空的,则不需流出水滴
  • 可以以任意速率流入水滴到漏桶(流入请求)
  • 如果流入水滴超出了桶的容量,则流入的水滴溢出了(新流入的请求被拒绝),则漏桶容量是不变的

漏桶可以看做固定容量、固定流出速率的队列,漏桶限制的是请求的流出速率,漏桶中装的是请求

因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流突发( burst )到端口速率.因此,漏桶算法对于存在突发特性的流量来说缺乏效率.

 

2.4. 令牌桶和漏桶的比较

 

令牌桶

漏桶

请求何时拒绝固定速率往桶中添加令牌,如果桶中令牌不够,则拒绝新请求流入请求速率任意,常量固定速率流出请求。当流入请求数积累到漏桶容量时,则拒绝新请求
速率限制限制平均流入速率,允许一定程度的突发请求(支持一次拿多个令牌)限制常量流出速率(流出速率是固定值),从而平滑突发流入速率

 

参考:

基于Redis的限流系统的设计

 

好的,我可以回答这个问题。为了防止用户暴力请求接口,我们可以在后端代码中加入限流的功能。具体实现方法如下: 1. 在 config 目录下新建一个文件 rate_limit.php,用于存放限流的配置信息。 2. 在 app 目录下新建一个 middleware 目录,用于存放中间件。 3. 在 middleware 目录下新建一个 RateLimitMiddleware.php 文件,用于实现限流的逻辑。 4. 在 app 目录下的 Http 目录下的 kernel.php 文件中注册中间件。 5. 在需要进行限流的接口中使用中间件。 具体代码实现可以参考以下示例: 1. rate_limit.php 文件内容如下: ```php <?php return [ // 每分钟最多请求次数 'limit' => 60, // 错误提示信息 'message' => '请求过于频繁,请稍后再试', ]; ``` 2. RateLimitMiddleware.php 文件内容如下: ```php <?php namespace app\middleware; use Closure; use think\facade\Cache; use think\facade\Config; use think\facade\Request; use think\Response; class RateLimitMiddleware { public function handle($request, Closure $next) { $key = 'rate_limit:' . Request::ip(); $limit = Config::get('rate_limit.limit'); $message = Config::get('rate_limit.message'); if (Cache::has($key)) { $count = Cache::get($key); if ($count >= $limit) { return new Response($message, 429); } Cache::inc($key); } else { Cache::set($key, 1, 60); } return $next($request); } } ``` 3. kernel.php 文件中注册中间件: ```php <?php namespace app; use app\middleware\RateLimitMiddleware; use think\App; use think\middleware\LoadLangPack; use think\middleware\SessionInit; class HttpKernel { public function __construct(App $app) { $this->app = $app; $this->middleware = [ LoadLangPack::class, SessionInit::class, RateLimitMiddleware::class, ]; } } ``` 4. 在需要进行限流的接口中使用中间件: ```php <?php namespace app\controller; use think\Controller; class Index extends Controller { public function index() { return 'Hello, World!'; } public function api() { return 'Hello, API!'; } protected $middleware = [ 'app\middleware\RateLimitMiddleware' => ['only' => ['api']], ]; } ``` 以上就是基于 thinkphp5 写一个防止用户暴力请求接口的实例的代码实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值