限流器
webman限流器,支持注解限流。
支持apcu、redis、memory驅(qū)動(dòng)。
源碼地址
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
{
// 默認(rèn)為IP限流,默認(rèn)單位時(shí)間為1秒
return '每個(gè)ip每秒最多10個(gè)請求';
}
#[RateLimiter(limit: 100, ttl: 60, key: RateLimiter::UID)]
public function search(): string
{
// key: RateLimiter::UID,以用戶ID為維度進(jìn)行限流,要求session('user.id')不為空
return '每個(gè)用戶60秒最多100次搜索';
}
#[RateLimiter(limit: 1, ttl: 60, key: RateLimiter::SID, message: '每人每分鐘只能發(fā)1次郵件')]
public function sendMail(): string
{
// key: RateLimiter::SID,以session_id為維度進(jìn)行限流
return '郵件發(fā)送成功';
}
#[RateLimiter(limit: 100, ttl: 24*60*60, key: 'coupon', message: '今天的優(yōu)惠券已經(jīng)發(fā)完,請明天再來')]
#[RateLimiter(limit: 1, ttl: 24*60*60, key: RateLimiter::UID, message: '每個(gè)用戶每天只能領(lǐng)取一次優(yōu)惠券')]
public function coupon(): string
{
// key: 'coupon', 這里coupon為自定義key,也就是全局以coupon為key進(jìn)行限流,每天最多發(fā)100張優(yōu)惠券
// 同時(shí)以用戶ID為維度進(jìn)行限流,每個(gè)用戶每天只能領(lǐng)取一次優(yōu)惠券
return '優(yōu)惠券發(fā)送成功';
}
#[RateLimiter(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: '每個(gè)手機(jī)號(hào)一天最多5條短信')]
public function sendSms2(): string
{
// 當(dāng)key為變量時(shí),可以使用[類, 靜態(tài)方法]的方式獲取key,例如[UserController::class, 'getMobile']會(huì)調(diào)用UserController的getMobile()方法的返回值為key
return '短信發(fā)送成功';
}
/**
* 自定義key,獲取手機(jī)號(hào),必須是靜態(tài)方法
* @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
{
// 超限默認(rèn)異常為Webman\RateLimiter\RateLimitException,可以通過exception參數(shù)更改
return 'ok';
}
}
說明
- 默認(rèn)單位時(shí)間間隔為1秒鐘
- 可以通過ttl設(shè)置單位時(shí)間間隔,例如
ttl:60
為60秒 - 默認(rèn)限流維度為IP限流(默認(rèn)
127.0.0.1
不限流,參見下面配置部分) - 內(nèi)置IP限流、UID限流(要求
session('user.id')
不為空),SID限流(根據(jù)session_id
限流) - 如果使用了nginx代理,IP限流時(shí)需要傳遞
X-Forwarded-For
頭,參見nginx代理 - 當(dāng)超限時(shí)會(huì)觸發(fā)
Webman\RateLimiter\RateLimitException
異常,可通過exception:xx
來自定義異常類 - 超限觸發(fā)異常時(shí),錯(cuò)誤信息默認(rèn)為
Too Many Requests
,可通過message:xx
自定義錯(cuò)誤信息 - 默認(rèn)錯(cuò)誤信息也可通過多語言來修改,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
接口
有時(shí)候開發(fā)者想直接在代碼中調(diào)用限流器,參考如下代碼
<?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, '每個(gè)手機(jī)號(hào)一天最多5條短信');
return '短信發(fā)送成功';
}
}
配置
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 時(shí)有效)
'ip_whitelist' => [
'127.0.0.1',
],
];
- enable: 是否開啟限流
- driver:
auto
apcu
memory
redis
中的一個(gè)值,使用auto
時(shí)會(huì)自動(dòng)在apcu
和memory
中選一個(gè)值 - stores:
redis
配置,connection
對應(yīng)config/redis.php
中對應(yīng)的key
- ip_whitelist: 白名單的ip不會(huì)被限流(只在key為
RateLimiter::IP
時(shí)有效)
driver選擇
memory
-
介紹
無需安裝任何擴(kuò)展,性能最好。 -
使用限制
限流只對當(dāng)前進(jìn)程有效,多個(gè)進(jìn)程間不共享限流數(shù)據(jù),同時(shí)也不支持集群限流。 -
適用場景
windows開發(fā)環(huán)境;不需要嚴(yán)格限流的業(yè)務(wù);抵御CC攻擊時(shí)。
apcu
-
安裝擴(kuò)展
需要安裝apcu擴(kuò)展,并且php.ini中設(shè)置apc.enabled=1 apc.enable_cli=1
如果不知道php.ini位置,可以通過命令
php --ini
尋找php.ini的位置 -
介紹
性能略低于memory,支持多進(jìn)程共享限流數(shù)據(jù)。 -
使用限制
不支持集群 -
適用場景
任何開發(fā)環(huán)境;線上單機(jī)限流場景;集群不需要嚴(yán)格限流的場景;抵御CC攻擊。
redis
-
依賴
需要安裝redis擴(kuò)展,并安裝Redis組件,安裝命令composer require -W illuminate/redis illuminate/events
-
介紹
性能低于apcu,支持單機(jī)也支持集群精確限流 -
適用場景
開發(fā)環(huán)境;線上單機(jī)環(huán)境;集群環(huán)境