layout: posttitle: PHP 滑动窗口限流算法
subtitle: PHP 滑动窗口限流算法
date: 2020-04-24
author: he xiaodong
header-img: img/default-post-bg.jpg
catalog: true
tags:
- Go
- PHP
- LeetCode
- 滑动窗口
- 限流

代码在 Laravel 6.0 上测试成功

滑动窗口其实就是 细分之后的计数器
2020-04-24-PHP 滑动窗口限流算法 - 图1
这样假设, 先把一分钟划分成6段! 也就是10s一个段!在第一段里,假如请求61次,那么直接触发了规则!肯定就过不去了!如果只请求了1次!则是正常的! 当时间走到第二个段里,即10s~20s这段范围里,我请求数不能超过总的限定条件,且当前段的请求数量 加上 之前段的总数量也不能超过总限定数量!

当时间到了50s~60s,依然是一样!

如果过了60s,所以请求数都是正常的,则把划分段往右移一段!那么此时的6个分段是 10 ~ 20,20 ~ 30,30 ~ 40,40 ~ 50,50 ~ 60,60 ~ 70

然后统计规则还跟上面一样!

所以,只有划分的越细,请求限制越平滑!

PHP 实现

  1. class SlidingWindow
  2. {
  3. protected $timeStamp;
  4. // 限定时间内请求的最多次数
  5. protected $limitCount = 100;
  6. // 时间间隔
  7. protected $interval = 10;
  8. // 请求数
  9. protected $reqCount = 0;
  10. // 分成多少份
  11. protected $count = 6;
  12. public function __construct()
  13. {
  14. $this->timeStamp = strtotime(date('Y-m-d H:i').':00');
  15. $this->reqCount = \Cache::get('reqCount');
  16. }
  17. public function grant()
  18. {
  19. $allCounter = \Cache::get('allCounterArr');
  20. $nowMinute = date('Y-m-d H:i');
  21. if (empty($allCounter)) {
  22. for ($i = 0; $i < $this->count; $i++) {
  23. $key = date('Y-m-d H:i', strtotime($nowMinute.':00') + ($i * 60));
  24. $allCounter[$key] = 0;
  25. }
  26. \Cache::put('allCounterArr', $allCounter, 10);
  27. }
  28. // 当所有间隔的总和大于限制条数 开始限流 || 当前分区请求量大于限定条数, 开始限流
  29. if (array_sum($allCounter) > $this->limitCount || $allCounter[$nowMinute] > $this->limitCount)
  30. {
  31. return false;
  32. }
  33. $allCounter[$nowMinute]++;
  34. // 如果当前区块是最后一块,那么整体右移一个区块
  35. if (end($allCounter) === $nowMinute) {
  36. array_shift($allCounter);
  37. $allCounter[date('Y-m-d H:i', strtotime($nowMinute.':00') + 60)] = 0;
  38. }
  39. \Cache::put('allCounterArr', $allCounter, 10);
  40. return true;
  41. }
  42. public function test()
  43. {
  44. $res = $this->grant();
  45. if ($res) {
  46. echo 1 . PHP_EOL;
  47. // 执行正常程序
  48. } else {
  49. echo 0 . PHP_EOL;
  50. // 进行限流
  51. }
  52. }
  53. }

调用实例:

  1. Route::get('2', function () {
  2. $slide = new SlidingWindow();
  3. for ($i = 0; $i < 106; $i++) {
  4. $slide->test();
  5. }
  6. });

参考文章:

  1. 计数器、滑动窗口、漏桶、令牌算法比较和伪代码实现

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券