业务限流策略:从网关到事后防护的三级防护方案

业务限流策略:从网关到事后防护的三级防护方案

目录

  1. 核心设计思想:木桶限流模型
  2. 一级防护:网关限流(Kong)
  3. 二级防护:业务中间件限流(PHP ThinkPHP)
  4. 三级防护:事后审计与端口保护(Fail2ban)

核心设计思想:木桶限流模型

限流的核心逻辑基于「木桶策略」:

  • 定义一个「时间盒子」(窗口时间),设定单位时间内允许通过的最大请求次数(限流阈值);
  • 当请求次数达到阈值时,触发限流机制,拒绝后续请求;
  • 「时间盒子」到期后,计数自动重置,开启新的窗口周期,恢复接收请求。

本次方案通过三级防护实现全链路限流,兼顾性能与安全性:

  1. 网关限流:请求入口层拦截,过滤海量恶意请求;
  2. 中间件限流:业务层精细化控制,适配业务场景;
  3. 事后封禁:针对异常行为审计,长期阻断风险源。

一级防护:网关限流(Kong)

API网关作为请求入口,承担第一重限流职责,选择Kong网关基于其高性能、插件化特性,可快速集成限流功能。

架构示意图

Kong网关架构图

RateLimit插件配置

通过Kong的RateLimit插件实现多维度限流控制,当前配置如下(后续可根据业务调整):

  • 1秒内最多3次请求
  • 1分钟内最多60次请求
  • 1小时内最多600次请求

注:当前配置阈值偏高,实际生产环境需根据业务QPS合理下调。

插件配置界面

Kong RateLimit插件配置界面

限流触发效果

当请求频率超过配置阈值时,网关直接返回限流响应,拦截请求进入业务层:
Kong限流触发效果

二级防护:业务中间件限流(PHP ThinkPHP)

中间件限流作为网关限流的补充,针对已进入业务层的请求进行精细化控制。考虑到维护复杂度,采用固定窗口限流策略,避免复杂的梯度配置。

1. 创建中间件

通过ThinkPHP命令行快速生成限流中间件:

1
php think make:middleware RateLimit

中间件生成结果

RateLimit中间件生成成功

2. 核心逻辑:固定窗口限流

基于「木桶模型」设计,核心参数:

  • 窗口时间($window):60秒(固定窗口,避免频繁调整)
  • 限流阈值($limit):60次/分钟(单IP/用户的最大请求频率)

限流逻辑流程

  1. 首个请求进入时,开启60秒时间窗口,计数初始化为1;
  2. 窗口周期内,每接收一次请求,计数+1;
  3. 计数达到60次时,后续请求返回429(Too Many Requests);
  4. 窗口到期后,计数重置为0,开启新窗口。

3. 完整实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?php
namespace app\middleware;

use Closure;
use think\App;
use think\Cache;
use think\facade\Request;
use think\facade\Response;

class RateLimit
{
protected $app;
protected $cache;

/**
* 构造函数:注入应用实例和缓存实例
* @param App $app
* @param Cache $cache
*/
public function __construct(App $app, Cache $cache)
{
$this->app = $app;
$this->cache = $cache;
}

/**
* 中间件处理逻辑
* @param $request 请求对象
* @param Closure $next 下一个中间件/控制器
* @param int $window 时间窗口(秒),默认60秒
* @param int $limit 窗口内最大请求数,默认60次
* @return Response
*/
public function handle($request, Closure $next, int $window = 60, int $limit = 60)
{
// 获取限流唯一标识(用户ID优先,无则用IP)
$key = $this->getLimitKey();

// 从缓存获取当前请求计数(默认0)
$count = $this->cache->get($key, 0);

// 超过阈值则返回限流响应
if ($count >= $limit) {
return Response::create([
'code' => 429,
'msg' => "请求过于频繁,请 {$window} 秒后再试",
'data' => []
])->header(['Retry-After' => $window]); // 告知客户端重试时间
}

// 首次请求:初始化缓存并设置过期时间
if ($count === 0) {
$this->cache->set($key, 1, $window);
} else {
// 非首次请求:计数+1
$this->cache->inc($key);
}

// 放行请求到下一个中间件/控制器
return $next($request);
}

/**
* 生成限流唯一标识(用户维度优先,保障登录用户体验)
* @return string
*/
protected function getLimitKey()
{
$ip = Request::ip(); // 获取客户端IP(支持XFF头解析)
$userId = $this->app->session->get('user_id'); // 获取登录用户ID

return $userId ? "rate_limit:user:{$userId}" : "rate_limit:ip:{$ip}";
}
}

4. 配置说明

  • 限流维度:支持IP维度(未登录用户)和用户ID维度(已登录用户),避免误限合法用户;
  • 缓存依赖:基于ThinkPHP缓存组件,支持Redis、Memcached等分布式缓存,适配集群部署;
  • 响应规范:返回429状态码+Retry-After响应头,符合HTTP标准,便于客户端适配。

三级防护:事后审计与端口保护(Fail2ban)

针对端口扫描、暴力破解等恶意行为,通过Fail2ban进行日志审计和IP封禁,形成事后防护屏障。

1. 安装Fail2ban

1
apt update && apt install -y fail2ban

安装成功界面

Fail2ban安装成功

2. 端口扫描防护配置

步骤1:创建过滤规则配置文件

1
vim /etc/fail2ban/filter.d/tp8-6060.conf

步骤2:配置防护规则

1
2
3
4
5
6
7
8
9
[tp8-6060-port]
enabled = true # 启用该规则
filter = tp8-6060 # 过滤规则名称(需与配置文件名一致)
findtime = 300 # 检测周期(秒):5分钟内
maxretry = 5 # 最大尝试次数:5次
bantime = 1800 # 封禁时间(秒):30分钟
ignoreip = 127.0.0.1 # 白名单IP:本地回环地址
port = 6060 # 防护端口:业务应用端口
action = iptables-multiport # 封禁方式:通过iptables拦截

配置文件编辑界面

Fail2ban端口防护配置

3. 生效配置

1
2
3
4
5
6
7
8
# 重启Fail2ban服务
systemctl restart fail2ban

# 查看服务状态
systemctl status fail2ban

# 查看封禁列表
fail2ban-client status tp8-6060-port

4. 核心参数说明

参数 含义 推荐值
findtime 异常行为检测周期(秒) 300(5分钟)
maxretry 周期内最大允许尝试次数 3-5次
bantime IP封禁时长(秒) 1800-3600(30分钟-1小时)
ignoreip 白名单IP(避免误封) 127.0.0.1、内网IP段
action 封禁方式 iptables-multiport(多端口支持)

方案优势总结

  1. 分层防护:网关+中间件+事后封禁,覆盖请求全链路,避免单点失效;
  2. 易维护性:中间件采用固定窗口策略,配置简单,降低运维成本;
  3. 灵活性:Kong网关支持多维度限流,中间件支持用户/IP维度区分,适配不同场景;
  4. 安全性:Fail2ban针对底层端口攻击,形成最后一道防护屏障。