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 theo session_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 qua exception: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ằng message: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ụng auto sẽ tự động chọn một giá trị trong apcumemory
  • stores: Cấu hình cho redis, connection tương ứng với key trong config/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.ini

    apc.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 đặt

    composer 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