写点什么

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

  • 2025-06-13
    福建
  • 本文字数:4089 字

    阅读完需:约 13 分钟

今天我们不聊风花雪月,只讲这个让无数开发者夜不能寐的终极命题:当恶意流量如海啸般扑来,如何守住你的系统防线?


有些小伙伴在工作中可能经历过接口被刷的噩梦,但百万 QPS 量级的攻击完全是另一个维度的战争。


今天这篇文章跟大家一起聊聊接口被刷百万 QPS,如何防御,希望对你会有所帮助。


为什么百万 QPS 如此致命?


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



攻击者三大核心武器:

  1. IP 海洋战术:10 万+代理 IP 池动态轮转,传统 IP 限流失效。

  2. 设备克隆技术:伪造浏览器指纹,模拟真实设备行为。

  3. 协议级精准攻击:精心构造的 HTTP 请求,绕过基础 WAF 规则。


系统崩溃的致命链反应:

  • 线程池 100%占用 → 新请求排队超时

  • 数据库连接耗尽 → SQL 执行阻塞

  • Redis 响应飙升 → 缓存穿透雪崩

  • 微服务连环熔断 → 服务不可用


那么,我们该如何防御呢?


第一道防线:基础限流与熔断


1. 网关层限流


我们需要在网关层做限流,目前主流的解决方案是:Nginx + Lua。


下面是 Nginx 的限流配置:

location /api/payment {    access_by_lua_block {        local limiter = require "resty.limit.req"        -- 令牌桶配置:1000QPS + 2000突发容量        local lim, err = limiter.new("payment_limit", 1000, 2000)        if not lim then            ngx.log(ngx.ERR, "限流器初始化失败: ", err)            return ngx.exit(500)        end                -- 基于客户端IP限流        local key = ngx.var.remote_addr        local delay, err = lim:incoming(key, true)                if not delay then            if err == "rejected" then                -- 返回429状态码+JSON错误信息                ngx.header.content_type = "application/json"                ngx.status = 429                ngx.say([[{"code":429,"msg":"请求过于频繁"}]])                return ngx.exit(429)            end            ngx.log(ngx.ERR, "限流错误: ", err)            return ngx.exit(500)        end    }}
复制代码


代码解析:

  1. 使用 OpenResty 的lua-resty-limit-req模块

  2. 令牌桶算法:1000QPS 常规流量 + 2000 突发流量缓冲

  3. 基于客户端 IP 维度限流

  4. 超出限制返回 429 状态码和 JSON 格式错误


2. 分布式熔断


面对大流量时,我们需要增加分布式熔断机制,比如使用 Sentinel 集群流控。


下面是 Sentinel 集群的流控配置:

public class SentinelConfig {    @PostConstruct    public void initFlowRules() {        // 创建集群流控规则        ClusterFlowRule rule = new ClusterFlowRule();        rule.setResource("createOrder"); // 受保护资源        rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // QPS限流        rule.setCount(50000); // 集群阈值5万QPS        rule.setClusterMode(true); // 开启集群模式        rule.setClusterConfig(new ClusterRuleConfig()            .setFlowId(123) // 全局唯一ID            .setThresholdType(1) // 全局阈值        );                // 注册规则        ClusterFlowRuleManager.loadRules(Collections.singletonList(rule));    }}
复制代码


流程图如下:



实现原理:

  1. Token Server 集中管理全集群流量配额

  2. 网关节点实时向 Token Server 申请令牌

  3. 当集群总 QPS 超过阈值时,按比例限制各节点流量

  4. 避免单节点限流导致的集群流量不均衡问题


第二道防线:设备指纹与行为分析


1. 浏览器指纹生成


前端可以在浏览器上生成指纹,即使客户端 IP 换了,但相同设备的指纹还是一样的。


前端设备指纹生成方案,这里使用了 Canvas+WebGL。


// 前端设备指纹生成方案function generateDeviceFingerprint() {  // 1. 获取基础设备信息  const baseInfo = [    navigator.userAgent,    navigator.platform,    screen.width + 'x' + screen.height,    navigator.language  ].join('|');    // 2. 生成Canvas指纹  const canvas = document.createElement('canvas');  const ctx = canvas.getContext('2d');  ctx.fillStyle = '#f60';  ctx.fillRect(0, 0, 100, 30);  ctx.fillStyle = '#069';  ctx.font = '16px Arial';  ctx.fillText('防御即艺术', 10, 20);  const canvasData = canvas.toDataURL();    // 3. 生成WebGL指纹  const gl = canvas.getContext('webgl');  const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');  const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);    // 4. 组合生成最终指纹  const fingerprint = md5(baseInfo + canvasData + renderer);  return fingerprint;}
复制代码


指纹特性分析:

  • 稳定性:相同设备多次生成一致性 > 98%

  • 唯一性:不同设备碰撞概率 < 0.1%

  • 隐蔽性:用户无感知,无法简单清除


2. 行为分析模型


我们还可以分析用户的行为。


使用下面的鼠标行为分析引擎:


import numpy as np
def analyze_mouse_behavior(move_events): """ 分析鼠标移动行为特征 :param move_events: 鼠标移动事件列表 [{'x':100, 'y':200, 't':1680000000}, ...] :return: 异常概率(0-1) """ # 1. 计算移动速度序列 speeds = [] for i in range(1, len(move_events)): prev = move_events[i-1] curr = move_events[i] dx = curr['x'] - prev['x'] dy = curr['y'] - prev['y'] distance = (dx**2 + dy**2) ** 0.5 time_diff = curr['t'] - prev['t'] # 防止除零 speed = distance / max(0.001, time_diff) speeds.append(speed) # 2. 计算加速度变化 accelerations = [] for i in range(1, len(speeds)): acc = speeds[i] - speeds[i-1] accelerations.append(acc) # 3. 提取关键特征 features = { 'speed_mean': np.mean(speeds), 'speed_std': np.std(speeds), 'acc_max': max(accelerations), 'acc_std': np.std(accelerations), 'linearity': calc_linearity(move_events) } # 4. 使用预训练模型预测 return risk_model.predict([features])
复制代码


行为特征维度:

  1. 移动速度:机器人速度恒定,真人波动大

  2. 加速度:机器人加速度变化呈锯齿状

  3. 移动轨迹线性度:机器人多为直线运动

  4. 操作间隔:机器人操作间隔高度一致


第三道防线:动态规则引擎


1. 实时规则配置


我们还可以使用动态规则引擎(比如:Drools 引擎),可以配置风控规则。

Drools 风控规则示例:


rule "高频访问敏感接口"    // 规则元数据    salience 100  // 优先级    no-loop true  // 防止规则循环触发        // 条件部分    when        $req : Request(            path == "/api/coupon/acquire", // 敏感接口            $uid : userId != null,        // 登录用户            $ip : clientIp        )                // 统计同一用户10秒内请求次数        accumulate(            Request(                userId == $uid,                path == "/api/coupon/acquire",                this != $req,  // 排除当前请求                $ts : timestamp            );            $count : count($ts),            $minTime : min($ts),            $maxTime : max($ts)        )                // 判断条件:10秒内超过30次请求        eval($count > 30 && ($maxTime - $minTime) < 10000)    then        // 执行动作:阻断并记录        insert(new BlockEvent($uid, $ip, "高频领券"));        $req.setBlock(true);end
复制代码


规则引擎优势:

  • 实时生效:新规则秒级推送

  • 复杂条件:支持多维度联合判断

  • 动态更新:无需重启服务


2. 多维关联分析模型


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



使用风险评分机制。

评分模型公式:

风险分 =   IP风险权重 × IP评分 +  设备风险权重 × 设备评分 +  行为异常权重 × 行为异常度 +  历史画像权重 × 历史风险值
复制代码


终极防御架构


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



核心组件解析:

1、流量清洗层(CDN)

  • 过滤静态资源请求

  • 吸收 70%以上流量冲击


2、安全防护层(网关集群)

  • 设备指纹生成:标记每个请求源

  • 分布式限流:集群级 QPS 控制

  • 规则引擎:实时判断风险


3、实时风控层(Flink 计算)

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


4、数据支撑层

  • Redis:存储实时风险画像

  • Flink:计算行为特征指标

  • 规则管理台:动态调整策略


血泪教训


1. IP 白名单的陷阱


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



使用设备指纹校验和行为分析。


2. 限流阈值静态设置的灾难


场景:设置固定 5000QPS 阈值

问题:大促时正常流量超阈值被误杀

优化方案

// 动态阈值调整算法public class DynamicThreshold {    // 基于历史流量自动调整    public static int calculateThreshold(String api) {        // 1. 获取上周同时段流量        double base = getHistoricalQps(api);         // 2. 考虑当日增长系数        double growth = getGrowthFactor();         // 3. 保留20%安全余量        return (int)(base * growth * 0.8);     }}
复制代码


3. 忽略带宽成本


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

应对策略

  • 前置 CDN 吸收静态流量

  • 配置云厂商 DDoS 防护服务

  • 设置带宽自动熔断机制


真正的防御不是让攻击无法发生,而是让攻击者付出十倍代价却一无所获。当你的防御成本低于对手的攻击成本时,战争就结束了。


文章转载自:苏三说技术

原文链接:https://www.cnblogs.com/12lisu/p/18925651

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

还未添加个人签名 2025-04-01 加入

还未添加个人简介

评论

发布
暂无评论
接口被刷百万QPS,怎么防?_数据库_电子尖叫食人鱼_InfoQ写作社区