SpringBoot接口限流的常用方案

摘要:本文主要介绍SpringBoot项目的接口限流方案,市面上常用有2种限流方案漏桶和令牌桶,大家常用的都是令牌桶,本文也只实现了令牌桶的方案。

漏桶和令牌桶说明

漏桶和令牌桶是最常用的两种限流算法,核心差异如下:

特性 漏桶限流(Leaky Bucket)​ 令牌桶限流(Token Bucket)​
核心逻辑 固定速率处理请求("漏水"),桶缓冲突发请求 固定速率生成令牌,请求需获取令牌才能处理
输出速率 严格固定(等于漏水速率) 允许突发(最大为桶容量 + 新生成的令牌数)
突发容忍度 不允许突发(超过桶容量的请求直接拒绝) 允许突发(桶中令牌足够时,可一次性处理多个请求)
资源利用率 可能较低(即使系统空闲,也需按固定速率处理) 较高(系统空闲时可积累令牌,突发时快速处理)
典型场景 严格要求速率稳定(如网络流量整形) 允许一定突发(如 API 接口限流)

项目基础配置

pom.xml

xml 复制代码
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.48.0</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>33.4.8-jre</version>
        </dependency>
    </dependencies>

application.yml

yaml 复制代码
spring:
  data:
    redis:
      host: redis
      port: 6379

启动类

typescript 复制代码
@SpringBootApplication
public class RateLimitApplication {

    public static void main(String[] args) {
        SpringApplication.run(RateLimitApplication.class, args);
    }
}

基于Redisson的令牌桶

在分布式系统中,使用 Redisson 实现并发限流是一种常见且高效的方式

RateType限流键(key)的设计

  • OVERALL模式 ​:限流键需全局唯一(如 global_api_limit),确保所有请求共享同一个桶。

  • PER_CLIENT模式 ​:限流键需能唯一标识客户端(如 user:${userId}ip:${clientIp}),确保每个客户端独立使用自己的桶。

less 复制代码
@Slf4j
@RestController
@RequestMapping(value = "redisson/rate-limit")
public class RedissonRateLimitController {

    @Autowired
    private RedissonClient redissonClient;

    @PostMapping("create-order")
    public Object createOrder(String info){
        RRateLimiter rateLimiter = getLimiter();
        boolean b = rateLimiter.tryAcquire();
        if(b) {
            log.info("允许访问");
            return "SUCCESS";
        }
        log.warn("限流了");
        throw new RuntimeException("请求太火爆");
    }

    /**
     * 获取限流器
     * @return
     */
    private RRateLimiter getLimiter() {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter("create-order");
        rateLimiter.trySetRate(RateType.PER_CLIENT, 20, Duration.ofSeconds(5L));
        return rateLimiter;
    }

}

测试结果

我单线程请求了8S,请求成功40

Guava的实现

Guava 的 RateLimiter是单机环境下基于令牌桶算法的轻量级限流工具,适用于控制接口请求频率、资源访问速率或任务执行频率等场景。以下是不同场景下的使用案例,包含基础用法、高级特性及实际业务集成。

接口说明

  • RateLimiter.create(10.0):创建一个每秒生成 10 个令牌的限流器(即允许每秒 10 个请求)。
  • RateLimiter rateLimiter = RateLimiter.create(20.0, 5, TimeUnit.SECONDS): //创建预热限流器:目标速率 20 个/秒,预热时间 5 秒(从 2 个/秒逐步提升到 20 个/秒)
  • rateLimiter.acquire(1):阻塞等待获取 1 个令牌(若当前无令牌,会等待直到新令牌生成)。返回值为等待时间(秒)
  • rateLimiter.tryAcquire(long permits, long timeout, TimeUnit unit):非阻塞获取

具体代码

less 复制代码
@Slf4j
@RestController
@RequestMapping(value = "guava/rate-limit")
public class GuavaRateLimitController {

    private static final RateLimiter rateLimiter = RateLimiter.create(5);

    @PostMapping("create-order")
    public Object createOrder(String info){
        boolean b = rateLimiter.tryAcquire();
        if(b) {
            log.info("允许访问");
            return "SUCCESS";
        }
        log.warn("限流了");
        throw new RuntimeException("请求太火爆");
    }


}

结果

8s内成功访问45个请求

基于nginx的限流

Nginx 限流核心模块:ngx_http_limit_req_module

Nginx 内置的 ngx_http_limit_req_module基于漏桶算法实现限流,通过以下两个核心指令控制:

  • limit_req_zone:定义限流的共享内存区和基础速率。

  • limit_req:在具体 locationserver块中应用限流规则。

(1)定义限流共享内存区(limit_req_zone

在 Nginx 主配置文件(nginx.conf)的 http块中,定义共享内存区存储限流状态:

ini 复制代码
http {
    # 定义限流共享内存区(名称=one,大小=10MB,速率=10请求/秒)
    limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;

    # 其他全局配置...
    include       mime.types;
    default_type  application/octet-stream;
}
  • $binary_remote_addr:限流的键(基于客户端 IP 地址)。

  • zone=one:10m:共享内存区名称为 one,大小为 10MB(根据客户端数量调整,10MB 约支持 16 万 IP)。

  • rate=10r/s:基础速率(每秒允许 10 个请求)。

(2)在 serverlocation中应用限流规则

在需要限流的站点配置(如 server块)中,使用 limit_req指令应用规则,并设置被限流时的响应:

ini 复制代码
server {
    listen       80;
    server_name  example.com;

    location /api/ {
        # 应用限流规则(使用上面定义的 zone=one)
        limit_req zone=one burst=20 nodelay;  # burst=突发容量,nodelay=不等待直接拒绝
        
        # 被限流时返回 429 状态码,并跳转到自定义错误页
        limit_req_status_code 429;
        error_page 429 /custom_limit_msg;

        # 正常请求的反向代理(示例)
        proxy_pass http://backend_server;
    }

    # 自定义限流消息体(返回 JSON)
    location = /custom_limit_msg {
        internal;  # 仅内部访问,禁止外部直接请求
        add_header Content-Type application/json;
        return 429 '{"code": 429, "message": "请求过于频繁,请稍后再试"}';
    }
}
关键参数说明
参数/指令 含义
limit_req_zone 定义限流的共享内存区、键(如 IP)和基础速率(rate=10r/s表示每秒 10 个请求)。
burst 突发容量(允许短时间内超过基础速率的请求数),例如 burst=20表示最多允许 20 个突发请求。
nodelay 若启用,被限流的请求直接拒绝​(不等待);若禁用(默认),请求会等待直到令牌可用。
limit_req_status_code 设置被限流时的 HTTP 状态码(如 429 Too Many Requests)。
error_page 自定义限流时的响应内容(如返回 JSON 消息体)。

高级限流场景

1. 按用户 ID 限流

若需按用户 ID 限流(如防止恶意用户高频请求),可将限流键改为用户 ID(需前端传递 X-User-ID头):

ini 复制代码
nginx
复制
http {
    # 限流键为 X-User-ID 头的值(需确保头存在)
    limit_req_zone $http_x_user_id zone=user_limit:10m rate=5r/s;

    server {
        location /api/user/ {
            # 校验 X-User-ID 头是否存在,不存在则拒绝
            if ($http_x_user_id = "") {
                return 400 "Missing X-User-ID header";
            }
            
            # 应用限流(键为用户 ID)
            limit_req zone=user_limit burst=10 nodelay;
            limit_req_status_code 429;
            error_page 429 /custom_limit_msg;
            
            proxy_pass http://backend_server;
        }
    }
}
相关推荐
树℡独5 小时前
ns-3仿真之应用层(五)
服务器·网络·tcp/ip·ns3
a41324476 小时前
ubuntu 25 安装vllm
linux·服务器·ubuntu·vllm
Configure-Handler6 小时前
buildroot System configuration
java·服务器·数据库
津津有味道6 小时前
易语言TCP服务端接收刷卡数据并向客户端读卡器发送指令
服务器·网络协议·tcp·易语言
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.7 小时前
Keepalived VIP迁移邮件告警配置指南
运维·服务器·笔记
Genie cloud7 小时前
1Panel SSL证书申请完整教程
服务器·网络协议·云计算·ssl
一点程序7 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
!chen8 小时前
linux服务器静默安装Oracle26ai
linux·运维·服务器
莫大3308 小时前
2核2G云服务器PHP8.5+MySQL9.0+Nginx(LNMP)安装WordPress网站详细教程
运维·服务器·nginx
2501_927773078 小时前
imx6驱动
linux·运维·服务器