跳到主要内容

限流保护机制

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考虑
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本文件面向 yudao-cloud 的限流保护机制,系统性阐述基于 Redisson 的分布式限流实现,覆盖以下内容:

  • 限流算法与策略:基于 Redisson 的令牌桶模型与滑动窗口思想的组合实践
  • RateLimiter 注解的使用与配置:限流阈值、时间窗口、拒绝策略与键空间粒度
  • 高并发性能优化:本地缓存、异步处理与批量限流思路
  • 监控与调优:指标采集、可视化与参数调优建议

项目结构

yudao-cloud 的限流能力位于 yudao-spring-boot-starter-protection 模块中,采用注解 + AOP + Redisson 的组合方式:

  • 注解层:定义限流注解与可选键解析器
  • AOP 层:拦截标注方法,计算限流键并执行限流判断
  • 数据访问层:封装 Redisson RRateLimiter 的统一 DAO
  • 配置层:装配切面、DAO 与多种键解析器 Bean

Mermaid Diagram Code:

graph TB
subgraph "限流模块"
A["RateLimiter 注解"]
B["RateLimiterAspect 切面"]
C["RateLimiterRedisDAO"]
D["RateLimiterKeyResolver 接口"]
E["默认/用户/IP/节点/表达式 键解析器"]
end
subgraph "外部依赖"
R["RedissonClient"]
end
A --> B
D --> B
E --> B
B --> C
C --> R

图表来源

章节来源

核心组件

  • RateLimiter 注解:定义限流的时间窗口、阈值、提示消息、键解析器与自定义键表达式
  • RateLimiterAspect:基于 AOP 在方法执行前进行限流判断
  • RateLimiterRedisDAO:封装 Redisson RRateLimiter 的 tryAcquire 与动态速率配置
  • RateLimiterKeyResolver 及其实现:提供多粒度的限流键生成策略
  • YudaoRateLimiterConfiguration:装配切面、DAO 与各键解析器 Bean

章节来源

架构总览

下图展示从方法调用到限流决策的关键流程。

Mermaid Diagram Code:

sequenceDiagram
participant Client as "客户端"
participant Controller as "业务控制器"
participant Aspect as "RateLimiterAspect"
participant Resolver as "KeyResolver"
participant DAO as "RateLimiterRedisDAO"
participant RL as "Redisson RRateLimiter"
Client->>Controller : "调用带 @RateLimiter 的方法"
Controller->>Aspect : "进入前置通知"
Aspect->>Resolver : "根据注解解析限流键"
Resolver-->>Aspect : "返回限流键"
Aspect->>DAO : "tryAcquire(键, 阈值, 时间, 单位)"
DAO->>RL : "获取/设置限流配置并尝试获取配额"
RL-->>DAO : "返回是否允许"
DAO-->>Aspect : "布尔结果"
alt "允许"
Aspect-->>Controller : "放行"
Controller-->>Client : "正常响应"
else "拒绝"
Aspect-->>Controller : "抛出限流异常"
Controller-->>Client : "错误响应"
end

图表来源

详细组件分析

注解与配置参数

  • time/timeUnit:时间窗口长度与单位,默认 1 秒
  • count:时间窗口内的最大请求数
  • message:限流时的提示消息,默认使用统一错误码
  • keyResolver:选择键解析器类型,默认全局级别
  • keyArg:当使用表达式键解析器时,用于指定 SpEL 表达式

章节来源

键解析器体系

  • 默认键解析器:以方法签名与参数拼接后做摘要,形成全局级限流键
  • 用户键解析器:在默认键基础上附加登录用户标识,形成用户级限流
  • IP 键解析器:在默认键基础上附加客户端 IP,形成 IP 级限流
  • 服务器节点键解析器:在默认键基础上附加主机与进程信息,形成节点级限流
  • 表达式键解析器:通过 SpEL 动态解析参数,形成灵活的自定义限流键

Mermaid Diagram Code:

classDiagram
class RateLimiterKeyResolver {
+resolver(joinPoint, rateLimiter) String
}
class DefaultRateLimiterKeyResolver
class UserRateLimiterKeyResolver
class ClientIpRateLimiterKeyResolver
class ServerNodeRateLimiterKeyResolver
class ExpressionRateLimiterKeyResolver
RateLimiterKeyResolver <|.. DefaultRateLimiterKeyResolver
RateLimiterKeyResolver <|.. UserRateLimiterKeyResolver
RateLimiterKeyResolver <|.. ClientIpRateLimiterKeyResolver
RateLimiterKeyResolver <|.. ServerNodeRateLimiterKeyResolver
RateLimiterKeyResolver <|.. ExpressionRateLimiterKeyResolver

图表来源

章节来源

限流算法与策略

  • 算法基础:基于 Redisson 的 RRateLimiter,其内部采用令牌桶模型,支持 OVERALL 类型的全局速率控制
  • 窗口与配额:通过 trySetRate 设置每 N 秒允许 count 个请求;每次 tryAcquire 消耗 1 个令牌
  • 动态配置:若限流器已存在且配置一致则复用,否则更新配置并设置过期时间

Mermaid Diagram Code:

flowchart TD
Start(["进入 tryAcquire"]) --> GetRL["获取/创建 RRateLimiter"]
GetRL --> HasCfg{"是否存在配置?"}
HasCfg --> |否| SetRate["设置速率: OVERALL(count, seconds)"]
HasCfg --> |是| CmpCfg{"配置是否一致?"}
CmpCfg --> |是| UseRL["直接使用"]
CmpCfg --> |否| UpdateRate["更新速率并设置过期"]
SetRate --> Acquire["tryAcquire()"]
UseRL --> Acquire
UpdateRate --> Acquire
Acquire --> Result{"是否允许?"}
Result --> |是| Allow["返回 true"]
Result --> |否| Deny["返回 false"]

图表来源

章节来源

AOP 切面与拒绝策略

  • 切入点:拦截带有 @RateLimiter 的方法
  • 执行顺序:先解析键,再调用 DAO 执行限流,最后根据结果决定放行或抛出限流异常
  • 拒绝策略:当 tryAcquire 返回 false 时,抛出统一的限流错误码与可选提示消息

Mermaid Diagram Code:

sequenceDiagram
participant A as "RateLimiterAspect"
participant K as "KeyResolver"
participant D as "RateLimiterRedisDAO"
A->>K : "resolver(joinPoint, annotation)"
K-->>A : "返回限流键"
A->>D : "tryAcquire(key, count, time, unit)"
D-->>A : "true/false"
alt "false"
A-->>A : "抛出限流异常"
else "true"
A-->>A : "放行"
end

图表来源

章节来源

配置与装配

  • 自动装配:在 Redis 自动配置之后注册切面、DAO 与各键解析器 Bean
  • 依赖注入:切面依赖键解析器集合与 DAO;DAO 依赖 RedissonClient

章节来源

与通用 Redis 工具的关系

  • RedisUtils 提供更底层的 rateLimiter 封装,便于在非注解场景快速使用
  • 两者共享相同的 Redisson RRateLimiter 能力,但注解模式更易用、可扩展性强

章节来源

依赖关系分析

  • 组件耦合:切面依赖 DAO 与键解析器;DAO 依赖 RedissonClient
  • 外部依赖:yudao-spring-boot-starter-redis 与 lock4j-redisson-spring-boot-starter(可选)
  • 自动装配顺序:确保 Redis 自动配置先于限流配置完成

Mermaid Diagram Code:

graph LR
POM["starter-protection 依赖"] --> WEB["yudao-spring-boot-starter-web(provided)"]
POM --> REDIS["yudao-spring-boot-starter-redis"]
POM --> LOCK["lock4j-redisson-spring-boot-starter(可选)"]
CFG["YudaoRateLimiterConfiguration"] --> ASPECT["RateLimiterAspect"]
CFG --> DAO["RateLimiterRedisDAO"]
CFG --> RESOLVERS["多个 KeyResolver Bean"]
ASPECT --> DAO
ASPECT --> RESOLVERS
DAO --> REDIS

图表来源

章节来源

性能考虑

  • 本地缓存与配置复用
    • DAO 在首次创建限流器后会缓存配置,后续命中相同配置直接复用,避免重复设置
    • 限流器按时间窗口过期,减少长期占用
  • 异步处理
    • 对于非关键路径的限流,可在业务侧结合异步任务或消息队列削峰填谷
  • 批量限流
    • 对批量接口建议使用更粗粒度的键(如用户级或 IP 级),并在网关层配合限流
  • Redis 性能
    • 合理设置过期时间与键空间,避免内存膨胀
    • 使用 Redis 集群时注意键分布与热点问题

章节来源

故障排查指南

  • 未生效
    • 确认方法上正确标注 @RateLimiter
    • 检查自动装配顺序:Redis 自动配置需先于限流配置
  • 限流键不正确
    • 核对 keyResolver 与 keyArg 的选择是否符合预期
    • 表达式键解析器需确保 SpEL 表达式能正确解析到目标参数
  • 频繁拒绝
    • 调整 time 与 count,增大时间窗口或提升阈值
    • 评估键粒度过细导致的误伤,必要时提升到用户级或节点级
  • 异常处理
    • 切面在拒绝时抛出统一限流错误码,可通过全局异常处理器统一返回

章节来源

结论

yudao-cloud 的限流保护机制以注解 + AOP + Redisson 为核心,提供了灵活的键空间粒度与简洁的配置方式。通过合理的键策略、参数调优与配套的异步/批量手段,可在高并发场景下有效保护系统稳定性与用户体验。

附录

使用示例与最佳实践

  • 全局限流:适用于通用接口,使用默认键解析器
  • 用户限流:针对登录用户,避免同一用户滥用
  • IP 限流:适用于匿名接口,抵御刷量
  • 节点限流:适用于网关或服务节点级限流
  • 自定义键:使用表达式键解析器,按业务维度灵活组合

章节来源

用户文档
AI 助手
Agent 列表
请选择一个 Agent 开始对话
AI 问答