Crater API限流实现:使用Redis与Lua脚本
限流方案概述
Crater作为开源发票管理系统,API接口需要保护机制防止滥用。基于Redis的令牌桶算法是实现限流的高效方案,通过Lua脚本保证原子性操作,支持分布式环境下的精确限流控制。
核心实现文件
系统限流功能主要通过以下文件实现:
Redis配置详解
在config/database.php中配置Redis连接信息:
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
],
Lua限流脚本
限流逻辑通过Lua脚本实现原子性操作,脚本存储在app/Services/Redis/RateLimitScript.php:
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])
local fill_time = capacity / rate
local ttl = math.floor(fill_time * 2)
local last_tokens = tonumber(redis.call("get", key..":tokens")) or capacity
local last_refreshed = tonumber(redis.call("get", key..":refreshed")) or 0
local delta = math.max(0, now - last_refreshed)
local filled_tokens = math.min(capacity, last_tokens + (delta * rate))
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
local allowed_num = 0
if allowed then
new_tokens = filled_tokens - requested
allowed_num = 1
end
redis.call("setex", key..":tokens", ttl, new_tokens)
redis.call("setex", key..":refreshed", ttl, now)
return {allowed_num, new_tokens}
限流中间件实现
app/Http/Middleware/RateLimitMiddleware.php实现请求拦截与限流判断:
public function handle($request, Closure $next)
{
$redis = Redis::connection();
$key = 'ratelimit:'. $request->ip();
$capacity = config('crater.rate_limit.capacity', 60);
$rate = config('crater.rate_limit.rate', 1);
$now = microtime(true);
$requested = 1;
$script = $this->getRateLimitScript();
$result = $redis->eval($script, 1, $key, $capacity, $rate, $now, $requested);
if ($result[0] == 0) {
return response()->json([
'error' => 'Rate limit exceeded',
'retry_after' => (int)ceil($capacity / $rate)
], 429);
}
return $next($request)
->header('X-RateLimit-Limit', $capacity)
->header('X-RateLimit-Remaining', $result[1]);
}
应用限流策略
在routes/api.php中为需要保护的路由添加限流中间件:
Route::middleware(['auth:sanctum', 'ratelimit'])->group(function () {
Route::apiResource('invoices', InvoiceController::class);
Route::apiResource('estimates', EstimateController::class);
Route::apiResource('customers', CustomerController::class);
});
配置参数说明
限流参数可通过config/crater.php进行调整:
'rate_limit' => [
'enabled' => env('RATE_LIMIT_ENABLED', true),
'capacity' => env('RATE_LIMIT_CAPACITY', 60), // 最大令牌数
'rate' => env('RATE_LIMIT_RATE', 1), // 每秒生成令牌数
],
监控与扩展
系统提供限流监控接口,通过app/Http/Controllers/Api/RateLimitController.php暴露当前限流状态,可结合Prometheus等工具实现可视化监控。对于高并发场景,可通过调整Redis集群配置和限流参数实现弹性扩展。
实现流程图
性能优化建议
- 使用Redis Pipeline减少网络往返
- 合理设置TTL避免键值堆积
- 针对不同API设置差异化限流策略
- 实现动态限流参数调整机制
- 考虑地理分布式限流方案
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



