CounterBehavior.php 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. <?php
  2. namespace common\behaviors;
  3. use Yii;
  4. use yii\base\Behavior;
  5. use yii\web\Controller;
  6. use yii\redis\Connection;
  7. use yii\web\TooManyRequestsHttpException;
  8. use common\helpers\DateHelper;
  9. /**
  10. * 计数器 - 限流
  11. *
  12. * Class CounterBehavior
  13. * @package common\behaviors
  14. * @author jianyan74 <751393839@qq.com>
  15. */
  16. class CounterBehavior extends Behavior
  17. {
  18. /**
  19. * 请求方法
  20. *
  21. * @var string
  22. */
  23. public $action = "*";
  24. /**
  25. * 规定时间内最大请求数量
  26. *
  27. * @var int
  28. */
  29. public $maxCount = 1000;
  30. /**
  31. * 用户id
  32. *
  33. * @var int
  34. */
  35. public $userId = 0;
  36. /**
  37. * 在60秒时间内
  38. *
  39. * @var float|int
  40. */
  41. public $period = 60 * 1000;
  42. /**
  43. * @return array
  44. */
  45. public function events()
  46. {
  47. return [Controller::EVENT_BEFORE_ACTION => 'beforeAction'];
  48. }
  49. /**
  50. * @param $event
  51. * @throws TooManyRequestsHttpException
  52. */
  53. public function beforeAction($event)
  54. {
  55. /** @var Connection $redis */
  56. $redis = Yii::$app->redis;
  57. // 限流: 用户 + 访问方法
  58. $key = sprintf('hist:%s:%s', $this->userId, Yii::$app->controller->route);
  59. $now = DateHelper::microtime(); // 毫秒时间戳
  60. $redis->zadd($key, $now, $now); // value 和 score 都使用毫秒时间戳
  61. $redis->zremrangebyscore($key, 0, $now - $this->period); // 移除时间窗口之前的行为记录,剩下的都是时间窗口内的
  62. if ($redis->zcard($key) > $this->maxCount) {
  63. throw new TooManyRequestsHttpException('服务器繁忙');
  64. }
  65. }
  66. }
粤ICP备19079148号