속도 제한기
webman 속도 제한기, 어노테이션 기반 제한 지원.
apcu, redis, memory 드라이버 지원.
소스 코드
https://github.com/webman-php/limiter
설치
composer require webman/limiter
사용
<?php
namespace app\controller;
use RuntimeException;
use support\limiter\annotation\Limit;
class UserController
{
#[Limit(limit: 10)]
public function index(): string
{
// 기본은 IP 제한, 기본 단위 시간은 1초
return 'IP당 초당 최대 10개 요청';
}
#[Limit(limit: 100, ttl: 60, key: Limit::UID)]
public function search(): string
{
// key: Limit::UID, 사용자 ID 기준 제한, session('user.id') 비어있지 않아야 함
return '사용자당 60초 최대 100회 검색';
}
#[Limit(limit: 1, ttl: 60, key: Limit::SID, message: '인당 분당 1통만 이메일 발송 가능')]
public function sendMail(): string
{
// key: Limit::SID, session_id 기준 제한
return '이메일 발송 성공';
}
#[Limit(limit: 100, ttl: 24*60*60, key: 'coupon', message: '오늘의 쿠폰은 소진되었습니다. 내일 다시 이용해 주세요')]
#[Limit(limit: 1, ttl: 24*60*60, key: Limit::UID, message: '사용자당 하루 1회만 쿠폰 수령 가능')]
public function coupon(): string
{
// key: 'coupon', 커스텀 key로 전역 제한, 하루 최대 100장 쿠폰
// 사용자 ID 기준 제한, 사용자당 하루 1회만 쿠폰 수령 가능
return '쿠폰 발송 성공';
}
#[Limit(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: '휴대폰 번호당 하루 최대 5통 SMS')]
public function sendSms2(): string
{
// key가 변수일 때 [클래스, 정적 메서드]로 key 획득, 예: [UserController::class, 'getMobile']은 UserController::getMobile() 반환값을 key로 사용
return 'SMS 발송 성공';
}
/**
* 커스텀 key, 휴대폰 번호 획득, 정적 메서드여야 함
* @return string
*/
public static function getMobile(): string
{
return request()->get('mobile');
}
#[Limit(limit: 1, ttl: 10, key: Limit::IP, message: '속도 제한', exception: RuntimeException::class)]
public function testException(): string
{
// 초과 시 기본 예외는 support\limiter\RateLimitException, exception 파라미터로 변경 가능
return 'ok';
}
}
설명
- 고정 윈도우 알고리즘 사용
- ttl 기본 단위 시간은 1초
- ttl로 단위 시간 설정, 예:
ttl:60은 60초 - 기본 제한 차원은 IP (기본
127.0.0.1은 제한 없음, 아래 설정 참조) - 내장: IP 제한, UID 제한 (
session('user.id')비어있지 않아야 함), SID 제한 (session_id기준) - nginx 프록시 사용 시 IP 제한에는
X-Forwarded-For헤더 전달, nginx 프록시 참조 - 초과 시
support\limiter\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
API
코드에서 직접 속도 제한기를 호출하는 예:
<?php
namespace app\controller;
use RuntimeException;
use support\limiter\Limiter;
class UserController {
public function sendSms(string $mobile): string
{
// 여기서 mobile을 key로 사용
Limiter::check($mobile, 5, 24*60*60, '휴대폰 번호당 하루 최대 5통 SMS');
return 'SMS 발송 성공';
}
}
설정
config/plugin/webman/limiter/app.php
<?php
use support\limiter\RateLimitException;
return [
'enable' => true,
'driver' => 'auto', // auto, apcu, memory, redis
'stores' => [
'redis' => [
'connection' => 'default',
]
],
// 이 IP들은 속도 제한 대상 아님 (key가 Limit::IP일 때만 유효)
'ip_whitelist' => [
'127.0.0.1',
],
'exception' => RateLimitException::class
];
- enable: 속도 제한 활성화 여부
- driver:
auto,apcu,memory,redis중 하나,auto는apcu(우선)와memory중 자동 선택 - stores: redis 설정,
connection은config/redis.php의 해당 key - ip_whitelist: 화이트리스트 IP는 속도 제한 대상 아님 (key가
Limit::IP일 때만 유효)
driver 선택
memory
-
소개
확장 불필요, 최고 성능. -
제한
현재 프로세스에만 유효, 프로세스 간 데이터 공유 없음, 클러스터 제한 미지원. -
적용 시나리오
Windows 개발 환경; 엄격한 제한 불필요한 업무; CC 공격 방어.
apcu
- 확장 설치
apcu 확장 필요, php.ini 설정:
apc.enabled=1
apc.enable_cli=1
php.ini 위치는 php --ini로 확인
-
소개
우수한 성능, 다중 프로세스 공유 지원. -
제한
클러스터 미지원 -
적용 시나리오
모든 개발 환경; 운영 단일 서버 제한; 클러스터에서 엄격한 제한 불필요; CC 공격 방어.
redis
- 의존
redis 확장과 Redis 컴포넌트 필요, 설치:
composer require -W webman/redis illuminate/events
-
소개
apcu보다 낮은 성능, 단일 서버 및 클러스터 정밀 제한 지원 -
적용 시나리오
개발 환경; 운영 단일 서버; 클러스터 환경