找回密码
 立即注册
首页 业界区 业界 接口被刷百万QPS,怎么防?

接口被刷百万QPS,怎么防?

伯斌 2025-6-12 15:45:28
大家好,我是苏三。
今天我们不聊风花雪月,只讲这个让无数开发者夜不能寐的终极命题:当恶意流量如海啸般扑来,如何守住你的系统防线?
有些小伙伴在工作中可能经历过接口被刷的噩梦,但百万QPS量级的攻击完全是另一个维度的战争。
今天这篇文章跟大家一起聊聊接口被刷百万QPS,如何防御,希望对你会有所帮助。
为什么百万QPS如此致命?

用一张图给解释一下百万QPS的危害:
1.webp

攻击者三大核心武器:

  • IP海洋战术:10万+代理IP池动态轮转,传统IP限流失效。
  • 设备克隆技术:伪造浏览器指纹,模拟真实设备行为。
  • 协议级精准攻击:精心构造的HTTP请求,绕过基础WAF规则。
系统崩溃的致命链反应:

  • 线程池100%占用 → 新请求排队超时
  • 数据库连接耗尽 →  SQL执行阻塞
  • Redis响应飙升 →  缓存穿透雪崩
  • 微服务连环熔断 →  服务不可用
那么,我们该如何防御呢?
第一道防线:基础限流与熔断

1. 网关层限流

我们需要在网关层做限流,目前主流的解决方案是:Nginx + Lua。
下面是Nginx的限流配置:
  1. location /api/payment {
  2.     access_by_lua_block {
  3.         local limiter = require "resty.limit.req"
  4.         -- 令牌桶配置:1000QPS + 2000突发容量
  5.         local lim, err = limiter.new("payment_limit", 1000, 2000)
  6.         if not lim then
  7.             ngx.log(ngx.ERR, "限流器初始化失败: ", err)
  8.             return ngx.exit(500)
  9.         end
  10.         
  11.         -- 基于客户端IP限流
  12.         local key = ngx.var.remote_addr
  13.         local delay, err = lim:incoming(key, true)
  14.         
  15.         if not delay then
  16.             if err == "rejected" then
  17.                 -- 返回429状态码+JSON错误信息
  18.                 ngx.header.content_type = "application/json"
  19.                 ngx.status = 429
  20.                 ngx.say([[{"code":429,"msg":"请求过于频繁"}]])
  21.                 return ngx.exit(429)
  22.             end
  23.             ngx.log(ngx.ERR, "限流错误: ", err)
  24.             return ngx.exit(500)
  25.         end
  26.     }
  27. }
复制代码
代码解析:

  • 使用OpenResty的lua-resty-limit-req模块
  • 令牌桶算法:1000QPS常规流量 + 2000突发流量缓冲
  • 基于客户端IP维度限流
  • 超出限制返回429状态码和JSON格式错误
2. 分布式熔断

面对大流量时,我们需要增加分布式熔断机制,比如使用Sentinel集群流控。
下面是Sentinel集群的流控配置:
  1. public class SentinelConfig {
  2.     @PostConstruct
  3.     public void initFlowRules() {
  4.         // 创建集群流控规则
  5.         ClusterFlowRule rule = new ClusterFlowRule();
  6.         rule.setResource("createOrder"); // 受保护资源
  7.         rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // QPS限流
  8.         rule.setCount(50000); // 集群阈值5万QPS
  9.         rule.setClusterMode(true); // 开启集群模式
  10.         rule.setClusterConfig(new ClusterRuleConfig()
  11.             .setFlowId(123) // 全局唯一ID
  12.             .setThresholdType(1) // 全局阈值
  13.         );
  14.         
  15.         // 注册规则
  16.         ClusterFlowRuleManager.loadRules(Collections.singletonList(rule));
  17.     }
  18. }
复制代码
流程图如下:
2.webp

实现原理:

  • Token Server集中管理全集群流量配额
  • 网关节点实时向Token Server申请令牌
  • 当集群总QPS超过阈值时,按比例限制各节点流量
  • 避免单节点限流导致的集群流量不均衡问题
第二道防线:设备指纹与行为分析

1. 浏览器指纹生成

前端可以在浏览器上生成指纹,即使客户端IP换了,但相同设备的指纹还是一样的。
前端设备指纹生成方案,这里使用了Canvas+WebGL。
  1. // 前端设备指纹生成方案
  2. function generateDeviceFingerprint() {
  3.   // 1. 获取基础设备信息
  4.   const baseInfo = [
  5.     navigator.userAgent,
  6.     navigator.platform,
  7.     screen.width + 'x' + screen.height,
  8.     navigator.language
  9.   ].join('|');
  10.   
  11.   // 2. 生成Canvas指纹
  12.   const canvas = document.createElement('canvas');
  13.   const ctx = canvas.getContext('2d');
  14.   ctx.fillStyle = '#f60';
  15.   ctx.fillRect(0, 0, 100, 30);
  16.   ctx.fillStyle = '#069';
  17.   ctx.font = '16px Arial';
  18.   ctx.fillText('防御即艺术', 10, 20);
  19.   const canvasData = canvas.toDataURL();
  20.   
  21.   // 3. 生成WebGL指纹
  22.   const gl = canvas.getContext('webgl');
  23.   const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
  24.   const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
  25.   
  26.   // 4. 组合生成最终指纹
  27.   const fingerprint = md5(baseInfo + canvasData + renderer);
  28.   return fingerprint;
  29. }
复制代码
指纹特性分析:

  • 稳定性:相同设备多次生成一致性 > 98%
  • 唯一性:不同设备碰撞概率 < 0.1%
  • 隐蔽性:用户无感知,无法简单清除
2. 行为分析模型

我们还可以分析用户的行为。
使用下面的鼠标行为分析引擎:
  1. import numpy as np
  2. def analyze_mouse_behavior(move_events):
  3.     """
  4.     分析鼠标移动行为特征
  5.     :param move_events: 鼠标移动事件列表 [{'x':100, 'y':200, 't':1680000000}, ...]
  6.     :return: 异常概率(0-1)
  7.     """
  8.     # 1. 计算移动速度序列
  9.     speeds = []
  10.     for i in range(1, len(move_events)):
  11.         prev = move_events[i-1]
  12.         curr = move_events[i]
  13.         dx = curr['x'] - prev['x']
  14.         dy = curr['y'] - prev['y']
  15.         distance = (dx**2 + dy**2) ** 0.5
  16.         time_diff = curr['t'] - prev['t']
  17.         # 防止除零
  18.         speed = distance / max(0.001, time_diff)
  19.         speeds.append(speed)
  20.    
  21.     # 2. 计算加速度变化
  22.     accelerations = []
  23.     for i in range(1, len(speeds)):
  24.         acc = speeds[i] - speeds[i-1]
  25.         accelerations.append(acc)
  26.    
  27.     # 3. 提取关键特征
  28.     features = {
  29.         'speed_mean': np.mean(speeds),
  30.         'speed_std': np.std(speeds),
  31.         'acc_max': max(accelerations),
  32.         'acc_std': np.std(accelerations),
  33.         'linearity': calc_linearity(move_events)
  34.     }
  35.    
  36.     # 4. 使用预训练模型预测
  37.     return risk_model.predict([features])
复制代码
行为特征维度:

  • 移动速度:机器人速度恒定,真人波动大
  • 加速度:机器人加速度变化呈锯齿状
  • 移动轨迹线性度:机器人多为直线运动
  • 操作间隔:机器人操作间隔高度一致
第三道防线:动态规则引擎

1. 实时规则配置

我们还可以使用动态规则引擎(比如:Drools引擎),可以配置风控规则。
Drools风控规则示例:
  1. rule "高频访问敏感接口"
  2.     // 规则元数据
  3.     salience 100  // 优先级
  4.     no-loop true  // 防止规则循环触发
  5.    
  6.     // 条件部分
  7.     when
  8.         $req : Request(
  9.             path == "/api/coupon/acquire", // 敏感接口
  10.             $uid : userId != null,        // 登录用户
  11.             $ip : clientIp
  12.         )
  13.         
  14.         // 统计同一用户10秒内请求次数
  15.         accumulate(
  16.             Request(
  17.                 userId == $uid,
  18.                 path == "/api/coupon/acquire",
  19.                 this != $req,  // 排除当前请求
  20.                 $ts : timestamp
  21.             );
  22.             $count : count($ts),
  23.             $minTime : min($ts),
  24.             $maxTime : max($ts)
  25.         )
  26.         
  27.         // 判断条件:10秒内超过30次请求
  28.         eval($count > 30 && ($maxTime - $minTime) < 10000)
  29.     then
  30.         // 执行动作:阻断并记录
  31.         insert(new BlockEvent($uid, $ip, "高频领券"));
  32.         $req.setBlock(true);
  33. end
复制代码
规则引擎优势:

  • 实时生效:新规则秒级推送
  • 复杂条件:支持多维度联合判断
  • 动态更新:无需重启服务
2. 多维关联分析模型

我们需要建立一套多维关联分析模型:
3.webp

使用风险评分机制。
评分模型公式:
  1. 风险分 =
  2.   IP风险权重 × IP评分 +
  3.   设备风险权重 × 设备评分 +
  4.   行为异常权重 × 行为异常度 +
  5.   历史画像权重 × 历史风险值
复制代码
终极防御架构

下面用用一张图总结一下百万QPS防御的架构体系:
4.webp

核心组件解析:

  • 流量清洗层(CDN)

    • 过滤静态资源请求
    • 吸收70%以上流量冲击

  • 安全防护层(网关集群)

    • 设备指纹生成:标记每个请求源
    • 分布式限流:集群级QPS控制
    • 规则引擎:实时判断风险

  • 实时风控层(Flink计算)
  1. // Flink实时风控处理
  2. riskStream
  3.   .keyBy(req => req.getDeviceId()) // 按设备ID分组
  4.   .timeWindow(Time.seconds(10))   // 10秒滚动窗口
  5.   .aggregate(new RiskAggregator)  // 聚合风险指标
  6.   .map(riskData => {
  7.     val score = riskModel.predict(riskData)
  8.     if(score > RISK_THRESHOLD) {
  9.       // 高风险请求阻断
  10.       blockRequest(riskData.getRequestId())
  11.     }
  12.   })
复制代码

  • 数据支撑层

    • Redis:存储实时风险画像
    • Flink:计算行为特征指标
    • 规则管理台:动态调整策略

血泪教训

1. IP白名单的陷阱

场景:将合作方IP加入白名单
灾难:攻击者入侵合作方服务器发起攻击
解决方案
5.webp

使用设备指纹校验和行为分析。
2. 限流阈值静态设置的灾难

场景:设置固定5000QPS阈值
问题:大促时正常流量超阈值被误杀
优化方案
  1. // 动态阈值调整算法
  2. public class DynamicThreshold {
  3.     // 基于历史流量自动调整
  4.     public static int calculateThreshold(String api) {
  5.         // 1. 获取上周同时段流量
  6.         double base = getHistoricalQps(api);
  7.         // 2. 考虑当日增长系数
  8.         double growth = getGrowthFactor();
  9.         // 3. 保留20%安全余量
  10.         return (int)(base * growth * 0.8);
  11.     }
  12. }
复制代码
3. 忽略带宽成本

教训:10Gbps流量攻击导致月度预算超支200%
应对策略

  • 前置CDN吸收静态流量
  • 配置云厂商DDoS防护服务
  • 设置带宽自动熔断机制
真正的防御不是让攻击无法发生,而是让攻击者付出十倍代价却一无所获。当你的防御成本低于对手的攻击成本时,战争就结束了。
最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,我的所有文章都会在公众号上首发,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册