限流器
webman 限流器,支持注解限流。
支持 apcu、redis、memory 驱动。
源码地址
https://github.com/webman-php/rate-limiter
安装
composer require webman/rate-limiter
使用
<?php
namespace app\controller;
use RuntimeException;
use Webman\RateLimiter\Annotation\RateLimiter;
class UserController
{#[RateLimiter(limit: 10)]
public function index(): string
{
// 默认为 IP 限流,默认单位时间为 1 秒
return '每个 ip 每秒最多 10 个请求';
}
#[RateLimiter(limit: 100, ttl: 60, key: RateLimiter::UID)]
public function search(): string
{// key: RateLimiter::UID,以用户 ID 为维度进行限流,要求 session('user.id')不为空
return '每个用户 60 秒最多 100 次搜索';
}
#[RateLimiter(limit: 1, ttl: 60, key: RateLimiter::SID, message: '每人每分钟只能发 1 次邮件')]
public function sendMail(): string
{
// key: RateLimiter::SID,以 session_id 为维度进行限流
return '邮件发送成功';
}
#[RateLimiter(limit: 100, ttl: 24*60*60, key: 'coupon', message: '今天的优惠券已经发完,请明天再来')]
#[RateLimiter(limit: 1, ttl: 24*60*60, key: RateLimiter::UID, message: '每个用户每天只能领取一次优惠券')]
public function coupon(): string
{
// key: 'coupon', 这里 coupon 为自定义 key,也就是全局以 coupon 为 key 进行限流,每天最多发 100 张优惠券
// 同时以用户 ID 为维度进行限流,每个用户每天只能领取一次优惠券
return '优惠券发送成功';
}
#[RateLimiter(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: '每个手机号一天最多 5 条短信')]
public function sendSms2(): string
{// 当 key 为变量时,可以使用 [类, 静态方法] 的方式获取 key,例如 [UserController::class, 'getMobile'] 会调用 UserController 的 getMobile()方法的返回值为 key
return '短信发送成功';
}
/**
* 自定义 key,获取手机号,必须是静态方法
* @return string
*/
public static function getMobile(): string
{return request()->get('mobile');
}
#[RateLimiter(limit: 1, ttl: 10, key: RateLimiter::IP, message: '频率受限', exception: RuntimeException::class)]
public function testException(): string
{
// 超限默认异常为 Webman\RateLimiter\RateLimitException,可以通过 exception 参数更改
return 'ok';
}
}
说明
- 默认单位时间间隔为 1 秒钟
- 可以通过 ttl 设置单位时间间隔,例如
ttl:60
为 60 秒 - 默认限流维度为 IP 限流 (默认
127.0.0.1
不限流,参见下面配置部分) - 内置 IP 限流、UID 限流 (要求
session('user.id')
不为空),SID 限流 (根据session_id
限流) - 如果使用了 nginx 代理,IP 限流时需要传递
X-Forwarded-For
头,参见nginx 代理 - 当超限时会触发
Webman\RateLimiter\RateLimitException
异常,可通过exception:xx
来自定义异常类 - 超限触发异常时,错误信息默认为
Too Many Requests
,可通过message:xx
自定义错误信息 - 默认错误信息也可通过 多语言 来修改,Linux 参考以下命令
composer require symfony/translation mkdir resource/translations/zh_CN/ -p echo "<?php return ['Too Many Requests' => '请求频率受限'];" > resource/translations/zh_CN/messages.php php start.php restart
接口
有时候开发者想直接在代码中调用限流器,参考如下代码
<?php
namespace app\controller;
use RuntimeException;
use Webman\RateLimiter\Limiter;
class UserController {public function sendSms(string $mobile): string
{
// 这里 mobile 作为 key
Limiter::check($mobile, 5, 24*60*60, '每个手机号一天最多 5 条短信');
return '短信发送成功';
}
}
配置
config/plugin/webman/rate-limiter/app.php
<?php
return [
'enable' => true,
'driver' => 'auto', // auto, apcu, memory, redis
'stores' => [
'redis' => ['connection' => 'default',]
],
// 这些 ip 的请求不做频率限制(只有在 key 为 RateLimiter::IP 时有效)
'ip_whitelist' => ['127.0.0.1',],
];
- enable: 是否开启限流
- driver:
auto
apcu
memory
redis
中的一个值,使用auto
时会自动在apcu
和memory
中选一个值 - stores:
redis
配置,connection
对应config/redis.php
中对应的key
- ip_whitelist: 白名单的 ip 不会被限流 (只在 key 为
RateLimiter::IP
时有效)
driver 选择
memory
-
介绍
无需安装任何扩展,性能最好。 -
使用限制
限流只对当前进程有效,多个进程间不共享限流数据,同时也不支持集群限流。 -
适用场景
windows 开发环境;不需要严格限流的业务;抵御 CC 攻击时。
apcu
-
安装扩展
需要安装 apcu 扩展,并且 php.ini 中设置apc.enabled=1 apc.enable_cli=1
如果不知道 php.ini 位置,可以通过命令
php --ini
寻找 php.ini 的位置 -
介绍
性能略低于 memory,支持多进程共享限流数据。 -
使用限制
不支持集群 -
适用场景
任何开发环境;线上单机限流场景;集群不需要严格限流的场景;抵御 CC 攻击。
redis
-
依赖
需要安装 redis 扩展,并安装 Redis 组件,安装命令composer require -W illuminate/redis illuminate/events
-
介绍
性能低于 apcu,支持单机也支持集群精确限流 -
适用场景
开发环境;线上单机环境;集群环境