TrafficShaper.php 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. <?php
  2. namespace common\components;
  3. use Yii;
  4. use yii\redis\Connection;
  5. /**
  6. * 令牌桶 - 限流
  7. *
  8. * 令牌桶算法 (Token Bucket) 和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解。
  9. * 随着时间流逝,系统会按恒定 1/QPS 时间间隔 (如果 QPS=100, 则间隔是 10ms) 往桶里加入 Token (想象和漏洞漏水相反,有个水龙头在不断的加水), 如果桶已经满了就不再加了。
  10. * 新请求来临时,会各自拿走一个 Token, 如果没有 Token 可拿了就阻塞或者拒绝服务.
  11. *
  12. * 令牌桶的另外一个好处是可以方便的改变速度。
  13. * 一旦需要提高速率,则按需提高放入桶中的令牌的速率。
  14. * 一般会定时 (比如 1000 毫秒) 往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量.
  15. *
  16. * Class TrafficShaper
  17. * @package common\components
  18. * @author jianyan74 <751393839@qq.com>
  19. */
  20. class TrafficShaper
  21. {
  22. /**
  23. * 令牌桶
  24. *
  25. * @var string
  26. */
  27. public $container;
  28. /**
  29. * 最大令牌数
  30. *
  31. * @var int
  32. */
  33. public $max;
  34. /**
  35. * @var Connection
  36. */
  37. protected $redis;
  38. /**
  39. * TrafficShaper constructor.
  40. * @param int $max
  41. * @param string $container
  42. */
  43. public function __construct($max = 300, $container = 'container')
  44. {
  45. $this->redis = Yii::$app->redis;
  46. $this->max = $max;
  47. $this->container = $container;
  48. }
  49. /**
  50. * 加入令牌
  51. *
  52. * 注意:需要加入定时任务,定时增加令牌数量
  53. *
  54. * @param int $num 加入的令牌数量
  55. * @return int 加入的数量
  56. */
  57. public function add($num = 0)
  58. {
  59. // 当前剩余令牌数
  60. $curnum = intval($this->redis->llen($this->container));
  61. // 最大令牌数
  62. $maxnum = intval($this->max);
  63. // 计算最大可加入的令牌数量,不能超过最大令牌数
  64. $num = $maxnum >= $curnum + $num ? $num : $maxnum - $curnum;
  65. // 加入令牌
  66. if ($num > 0) {
  67. $token = array_fill(0, $num, 1);
  68. $this->redis->lpush($this->container, ...$token);
  69. return $num;
  70. }
  71. return 0;
  72. }
  73. /**
  74. * 获取令牌
  75. *
  76. * @return bool
  77. */
  78. public function info()
  79. {
  80. return intval($this->redis->llen($this->container));
  81. }
  82. /**
  83. * 获取令牌
  84. *
  85. * @return bool
  86. */
  87. public function get()
  88. {
  89. return $this->redis->rpop($this->container) ? true : false;
  90. }
  91. /**
  92. * 重设令牌桶,填满令牌
  93. */
  94. public function reset()
  95. {
  96. $this->redis->lrem($this->container, 0, $this->max);
  97. $this->add($this->max);
  98. }
  99. }
粤ICP备19079148号