Bộ giới hạn lưu lượng
Bộ giới hạn lưu lượng webman, hỗ trợ giới hạn lưu lượng thông qua chú thích.
Hỗ trợ các trình điều khiển apcu, redis, memory.
Địa chỉ mã nguồn
https://github.com/webman-php/rate-limiter
Cài đặt
composer require webman/rate-limiter
Sử dụng
<?php
namespace app\controller;
use RuntimeException;
use Webman\RateLimiter\Annotation\RateLimiter;
class UserController
{
#[RateLimiter(limit: 10)]
public function index(): string
{
// Mặc định giới hạn lưu lượng theo IP, đơn vị thời gian mặc định là 1 giây
return 'Mỗi IP tối đa 10 yêu cầu mỗi giây';
}
#[RateLimiter(limit: 100, ttl: 60, key: RateLimiter::UID)]
public function search(): string
{
// key: RateLimiter::UID, giới hạn lưu lượng theo ID người dùng, yêu cầu session('user.id') không được rỗng
return 'Mỗi người dùng tối đa 100 lần tìm kiếm trong 60 giây';
}
#[RateLimiter(limit: 1, ttl: 60, key: RateLimiter::SID, message: 'Mỗi người chỉ có thể gửi 1 email mỗi phút')]
public function sendMail(): string
{
// key: RateLimiter::SID, giới hạn lưu lượng theo session_id
return 'Gửi email thành công';
}
#[RateLimiter(limit: 100, ttl: 24*60*60, key: 'coupon', message: 'Phiếu giảm giá hôm nay đã hết, vui lòng quay lại vào ngày mai')]
#[RateLimiter(limit: 1, ttl: 24*60*60, key: RateLimiter::UID, message: 'Mỗi người dùng chỉ có thể nhận 1 phiếu giảm giá mỗi ngày')]
public function coupon(): string
{
// key: 'coupon', tại đây coupon là key tùy chỉnh, tức là giới hạn lưu lượng toàn cầu bằng key coupon, mỗi ngày tối đa 100 phiếu giảm giá
// Đồng thời giới hạn lưu lượng theo ID người dùng, mỗi người dùng chỉ có thể nhận 1 phiếu giảm giá mỗi ngày
return 'Gửi phiếu giảm giá thành công';
}
#[RateLimiter(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: 'Mỗi số điện thoại tối đa 5 tin nhắn mỗi ngày')]
public function sendSms2(): string
{
// Khi key là biến, có thể sử dụng cách [class, phương thức tĩnh] để lấy key, ví dụ [UserController::class, 'getMobile'] sẽ gọi phương thức getMobile() của UserController
return 'Gửi tin nhắn thành công';
}
/**
* Key tùy chỉnh, lấy số điện thoại, phải là phương thức tĩnh
* @return string
*/
public static function getMobile(): string
{
return request()->get('mobile');
}
#[RateLimiter(limit: 1, ttl: 10, key: RateLimiter::IP, message: 'Tần suất bị hạn chế', exception: RuntimeException::class)]
public function testException(): string
{
// Khi vượt giới hạn, ngoại lệ mặc định là Webman\RateLimiter\RateLimitException, có thể thay đổi thông qua tham số exception
return 'ok';
}
}
Giải thích
- Đơn vị thời gian mặc định là 1 giây
- Có thể thiết lập khoảng thời gian đơn vị thông qua ttl, ví dụ
ttl:60
là 60 giây - Độ phân giải lưu lượng mặc định là theo IP (mặc định
127.0.0.1
không bị giới hạn, xem phần cấu hình bên dưới) - Hỗ trợ giới hạn lưu lượng theo IP, UID (yêu cầu
session('user.id')
không được rỗng), SID (giới hạn theosession_id
) - Nếu sử dụng proxy nginx, khi giới hạn theo IP cần truyền tham số
X-Forwarded-For
, xem proxy nginx - Khi vượt giới hạn sẽ kích hoạt ngoại lệ
Webman\RateLimiter\RateLimitException
, có thể tùy chỉnh lớp ngoại lệ thông quaexception:xx
- Khi kích hoạt ngoại lệ vượt giới hạn, thông điệp lỗi mặc định là
Too Many Requests
, có thể tùy chỉnh thông điệp lỗi bằngmessage:xx
- Thông điệp lỗi mặc định cũng có thể thay đổi thông qua đa ngôn ngữ, trên Linux tham khảo lệnh sau
composer require symfony/translation mkdir resource/translations/zh_CN/ -p echo "<?php return [ 'Too Many Requests' => 'Tần suất yêu cầu bị hạn chế' ];" > resource/translations/zh_CN/messages.php php start.php restart
Gọi API
Đôi khi các nhà phát triển muốn gọi trực tiếp bộ giới hạn lưu lượng trong mã, tham khảo đoạn mã sau
<?php
namespace app\controller;
use RuntimeException;
use Webman\RateLimiter\Limiter;
class UserController {
public function sendSms(string $mobile): string
{
// Tại đây mobile là key
Limiter::check($mobile, 5, 24*60*60, 'Mỗi số điện thoại tối đa 5 tin nhắn mỗi ngày');
return 'Gửi tin nhắn thành công';
}
}
Cấu hình
config/plugin/webman/rate-limiter/app.php
<?php
return [
'enable' => true,
'driver' => 'auto', // auto, apcu, memory, redis
'stores' => [
'redis' => [
'connection' => 'default',
]
],
// Các IP này sẽ không bị giới hạn tần suất (chỉ có hiệu lực khi key là RateLimiter::IP)
'ip_whitelist' => [
'127.0.0.1',
],
];
- enable: Có bật giới hạn lưu lượng không
- driver: Một trong các giá trị
auto
apcu
memory
redis
, khi sử dụngauto
sẽ tự động chọn một giá trị trongapcu
vàmemory
- stores: Cấu hình cho
redis
,connection
tương ứng vớikey
trongconfig/redis.php
- ip_whitelist: Các IP trong danh sách trắng sẽ không bị giới hạn lưu lượng (chỉ có hiệu lực khi key là
RateLimiter::IP
)
Lựa chọn driver
memory
-
Giới thiệu
Không cần cài đặt bất kỳ mở rộng nào, hiệu suất tốt nhất. -
Giới hạn sử dụng
Giới hạn lưu lượng chỉ có hiệu lực trên tiến trình hiện tại, dữ liệu giới hạn lưu lượng không chia sẻ giữa các tiến trình, cũng không hỗ trợ giới hạn lưu lượng cụm. -
Tình huống sử dụng
Môi trường phát triển trên windows; Doanh nghiệp không cần giới hạn lưu lượng nghiêm ngặt; Phòng ngừa tấn công CC.
apcu
-
Cài đặt mở rộng
Cần cài đặt mở rộng apcu, và cấu hình trong php.iniapc.enabled=1 apc.enable_cli=1
Nếu không biết vị trí của php.ini, có thể tìm bằng lệnh
php --ini
-
Giới thiệu
Hiệu suất thấp hơn memory một chút, hỗ trợ chia sẻ dữ liệu giới hạn lưu lượng giữa nhiều tiến trình. -
Giới hạn sử dụng
Không hỗ trợ cụm -
Tình huống sử dụng
Bất kỳ môi trường phát triển nào; Tình huống giới hạn lưu lượng trên máy chủ đơn; Tình huống cụm không cần giới hạn lưu lượng nghiêm ngặt; Phòng ngừa tấn công CC.
redis
-
Phụ thuộc
Cần cài đặt mở rộng redis, và cài đặt thành phần Redis, lệnh cài đặtcomposer require -W illuminate/redis illuminate/events
-
Giới thiệu
Hiệu suất thấp hơn apcu, hỗ trợ giới hạn lưu lượng chính xác trên máy đơn và cụm -
Tình huống sử dụng
Môi trường phát triển; Môi trường trên máy chủ đơn; Môi trường cụm