1. 在Laravel 中配置

在 app\Http\Kernel.php 中,默认添加到中间件组 api 下,1分钟60次。

2. 限流原理

  1. 获取唯一请求来源,进行唯一标识(key)
  2. 获取该请求请求次数 (hits)
  3. 判断是否超过最大限制
  4. 若达到上限,进入5。未达到,则进入6
  5. 丢出访问次数限制异常,结束请求。
  6. 首先判断hits 是否达到限制,若未达到,进入7。若达到,进入8。
  7. hits 进行计数 + 1,更新到缓存中。 若是第一次,则需要 hits = 1(次数),  并添加访问标识 key (1分钟)到缓存中,以标记请求周期。
  8. 请求次数已达到上限(hits >= 60),此时需要判断是否在周期范围内(1分钟),若在周期内,进入9;不在周期内,进入10.
  9. 此时请求处在 “1分钟内请求次数达到60次”,即达到限制,返回 false 。
  10. 此时请求处在 “不在1分钟内请求次数达到60次”,即不在周期内,需要重新计算周期。

3. 代码实现

3.1 业务逻辑在 ThrottleRequests -> handle 中实现。

  1. public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
  2. {
         // 获取唯一请求来源 2.1
  3. $key = $this->resolveRequestSignature($request);
  4. // 获取实际请求次数 2.2
  5. $maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);
  6. // 判断是否达到上限 2.3
  7. if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
    // 禁止请求 2.5
  8. throw $this->buildException($key, $maxAttempts);
  9. }
  10. // 2.7 计数
  11. $this->limiter->hit($key, $decayMinutes);
  12.  
  13. $response = $next($request);
  14.  
  15. return $this->addHeaders(
  16. $response, $maxAttempts,
  17. $this->calculateRemainingAttempts($key, $maxAttempts)
  18. );
  19. }

3.2 限流方法 在 Illuminate\Cache\RateLimiter 中

  1. <?php
  2.  
  3. namespace Illuminate\Cache;
  4.  
  5. use Illuminate\Support\InteractsWithTime;
  6. use Illuminate\Contracts\Cache\Repository as Cache;
  7.  
  8. class RateLimiter
  9. {
  10. use InteractsWithTime;
  11.  
  12. /**
  13. * The cache store implementation.
  14. *
  15. * @var \Illuminate\Contracts\Cache\Repository
  16. */
  17. protected $cache;
  18.  
  19. /**
  20. * Create a new rate limiter instance.
  21. *
  22. * @param \Illuminate\Contracts\Cache\Repository $cache
  23. * @return void
  24. */
  25.  
  26. // 初始化缓存
  27. public function __construct(Cache $cache)
  28. {
  29. $this->cache = $cache;
  30. }
  31.  
  32. /**
  33. * Determine if the given key has been "accessed" too many times.
  34. *
  35. * @param string $key
  36. * @param int $maxAttempts
  37. * @return bool
  38. */
  39.  
  40. // 判断是否达到上限
  41. public function tooManyAttempts($key, $maxAttempts)
  42. {
    // 判断次数,是否达到限制(60次)
  43. if ($this->attempts($key) >= $maxAttempts) {
    // 判断是否在限制周期内(1分钟内)
  44. if ($this->cache->has($key.':timer')) {
  45. return true;
  46. }
  47. // 2.10 重新计算周期
  48. $this->resetAttempts($key);
  49. }
  50.  
  51. return false;
  52. }
  53.  
  54. /**
  55. * Increment the counter for a given key for a given decay time.
  56. *
  57. * @param string $key
  58. * @param float|int $decayMinutes
  59. * @return int
  60. */
  61. public function hit($key, $decayMinutes = 1)
  62. {
    // 对应2.7 ,添加周期缓存
  63. $this->cache->add(
  64. $key.':timer', $this->availableAt($decayMinutes * 60), $decayMinutes
  65. );
  66. // 对应2.7 ,添加请求次数缓存
  67. $added = $this->cache->add($key, 0, $decayMinutes);
  68. // 请求次数 + 1
  69. $hits = (int) $this->cache->increment($key);
  70. // 更新次数
  71. if (! $added && $hits == 1) {
  72. $this->cache->put($key, 1, $decayMinutes);
  73. }
  74. // 返回次数
  75. return $hits;
  76. }
  77.  
  78. /**
  79. * Get the number of attempts for the given key.
  80. *
  81. * @param string $key
  82. * @return mixed
  83. */
  84.  
  85. // 获取请求次数,默认0
  86. public function attempts($key)
  87. {
  88. return $this->cache->get($key, 0);
  89. }
  90.  
  91. /**
  92. * Reset the number of attempts for the given key.
  93. *
  94. * @param string $key
  95. * @return mixed
  96. */
  97.  
  98. // 重置请求周期
  99. public function resetAttempts($key)
  100. {
  101. return $this->cache->forget($key);
  102. }
  103.  
  104. /**
  105. * Get the number of retries left for the given key.
  106. *
  107. * @param string $key
  108. * @param int $maxAttempts
  109. * @return int
  110. */
  111.  
  112. // 获取剩余次数
  113. public function retriesLeft($key, $maxAttempts)
  114. {
  115. $attempts = $this->attempts($key);
  116.  
  117. return $maxAttempts - $attempts;
  118. }
  119.  
  120. /**
  121. * Clear the hits and lockout timer for the given key.
  122. *
  123. * @param string $key
  124. * @return void
  125. */
  126.  
  127. // 清除请求计数和周期
  128. public function clear($key)
  129. {
  130. $this->resetAttempts($key);
  131.  
  132. $this->cache->forget($key.':timer');
  133. }
  134.  
  135. /**
  136. * Get the number of seconds until the "key" is accessible again.
  137. *
  138. * @param string $key
  139. * @return int
  140. */
  141.  
  142. // 判断是否在周期内
  143. public function availableIn($key)
  144. {
  145. return $this->cache->get($key.':timer') - $this->currentTime();
  146. }
  147. }

PS:  ThrottleRequestsWithRedis 和 ThrottleRequests 是相同的,区别在于前者指定 Redis 作为缓存,后者无限制(使用Laravel配置缓存)

Laravel 限流中间件 throttle 简析的更多相关文章

  1. Asp.Net Core 7 preview 4 重磅新特性--限流中间件

    前言 限流是应对流量暴增或某些用户恶意攻击等场景的重要手段之一,然而微软官方从未支持这一重要特性,AspNetCoreRateLimit这一第三方库限流库一般作为首选使用,然而其配置参数过于繁多,对使 ...

  2. 从-99打造Sentinel高可用集群限流中间件

    接上篇Sentinel集群限流探索,上次简单提到了集群限流的原理,然后用官方给的 demo 简单修改了一下,可以正常运行生效. 这一次需要更进一步,基于 Sentinel 实现内嵌式集群限流的高可用方 ...

  3. ASP.NET Core WebApi AspNetCoreRateLimit 限流中间件学习

    AspNetCoreRateLimit介绍: AspNetCoreRateLimit是ASP.NET核心速率限制框架,能够对WebApi,Mvc中控制限流,AspNetCoreRateLimit包包含 ...

  4. AspNetCore 限流中间件IpRateLimitMiddleware 介绍

    IpRateLimitMiddleware(Github: AspNetCoreRateLimit) 是ASPNETCore的一个限流的中间件,用于控制客户端调用API的频次, 如果客户端频繁访问服务 ...

  5. .NET服务治理之限流中间件-FireflySoft.RateLimit

    概述 FireflySoft.RateLimit自2021年1月发布第一个版本以来,经历了多次升级迭代,目前已经十分稳定,被很多开发者应用到了生产系统中,最新发布的版本是3.0.0. Github:h ...

  6. 【.NET Core项目实战-统一认证平台】第七章 网关篇-自定义客户端限流

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我介绍了如何在网关上增加自定义客户端授权功能,从设计到编码实现,一步一步详细讲解,相信大家也掌握了自定义中间件的开发技巧了,本篇我们 ...

  7. 漏桶、令牌桶限流的Go语言实现

    限流 限流又称为流量控制(流控),通常是指限制到达系统的并发请求数. 我们生活中也会经常遇到限流的场景,比如:某景区限制每日进入景区的游客数量为8万人:沙河地铁站早高峰通过站外排队逐一放行的方式限制同 ...

  8. AspNetCore添加API限流

    最近发现有客户在大量的请求我们的接口,出于性能考虑遂添加了请求频率限制. 由于我们接口请求的是.Net Core写的API网关,所以可以直接添加一个中间件,中间件中使用请求的地址当key,通过配置中心 ...

  9. 【Dnc.Api.Throttle】适用于.Net Core WebApi接口限流框架

    Dnc.Api.Throttle    适用于Dot Net Core的WebApi接口限流框架 使用Dnc.Api.Throttle可以使您轻松实现WebApi接口的限流管理.Dnc.Api.Thr ...

随机推荐

  1. python之爬虫_并发(串行、多线程、多进程、异步IO)

    并发 在编写爬虫时,性能的消耗主要在IO请求中,当单进程单线程模式下请求URL时必然会引起等待,从而使得请求整体变慢 import requests def fetch_async(url): res ...

  2. js中模拟a标签的点击事件

    var a = document.createElement('a'); a.target = "_blank"; a.href = "personal"; a ...

  3. json转对象

    1,引入依赖 <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib& ...

  4. 实验一linux 系统简介和实验二基本概念及操作

    作业 zy e

  5. IIs8 svc

    IIS8中添加WCF支持几种方法小结[图文] 方法一 最近在做Silverlight,Windows Phone应用移植到Windows 8平台,在IIS8中测试一些传统WCF服务应用,发现IIS8不 ...

  6. Codeforces Round #341 (Div. 2) E. Wet Shark and Blocks dp+矩阵加速

    题目链接: http://codeforces.com/problemset/problem/621/E E. Wet Shark and Blocks time limit per test2 se ...

  7. 校园跳蚤市场-Sprint计划

    一.现状 小组成员初步了解所做项目的大致内容,需要时间一步一步分析和规划. 二.部分需求索引卡 第一个阶段完成项目的其中一个模块(商品信息模块). 三.任务认领 产品负责人:林海信 Master:何武 ...

  8. thinkphp学习3-模板与视图

    1.模板赋值 如果要在模板中输出变量,必须在在控制器中把变量传递给模板,系统提供了assign方法对模板变量赋值,无论何种变量类型都统一使用assign赋值. $this->assign('na ...

  9. SQL语句中 chinese_prc_CS_AI_WS 以及replace用法

          Select * from [DBData].[dbo].[T_Student] where Name='lilei'    查询结果如下:   结论:由查询结果可知 SQL Server ...

  10. 014 C语言文法定义与C程序的推导过程