跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

常见限流算法介绍及 Redisson 分布式限流实现

固定窗口、滑动窗口、漏桶和令牌桶四种常见限流算法的原理、优缺点及适用场景。单机限流可使用 Guava RateLimiter,分布式限流推荐使用 Redisson 或网关工具如 Sentinel。重点讲解了基于 Redisson 的分布式限流实现,包括依赖引入、配置类编写以及使用新 API(Duration)设置限流速率的方法。通过示例展示了如何在 Controller 层集成限流逻辑,确保服务在高并发下的稳定性。

萤火微光发布于 2026/3/30更新于 2026/7/1859 浏览
常见限流算法介绍及 Redisson 分布式限流实现

本文介绍了四种常见的限流算法及其实现方式。固定窗口限流简单但存在临界突刺问题;滑动窗口通过时间片滑动解决突刺问题,但滑动单位选择困难;漏桶算法以固定速率处理请求,能削峰缓冲但不够灵活;令牌桶算法允许突发流量,并发性能更好但时间单位选择仍需考量。实现层面,单机限流可使用 Guava 的 RateLimiter,分布式限流推荐 Redisson 或网关层工具如 Sentinel。提供了 Redisson 的配置示例和两种限流设置方式,建议采用基于 Duration 的新 API 实现更简洁的限流控制。

限流算法介绍

1、固定窗口限流

在单位时间内允许部分操作。

比如,单位时间(窗口)为 1 小时,一小时内只能有 100 个用户能访问,如果在一小时内就已经达到了次数上限 100,那么这时不论来多少请求都会被拒绝掉,也就是说 1 小时内设定了多少次就是只能有多少次,只有到 1 小时才会重置次数。但是它有一个突刺问题,例如在前 59 分钟都没有一个用户进行访问,在 59 分钟的时候 100 个用户同时访问,然后 1 小时 01 分时候又有 100 个用户都访问了,那就是 2 分钟有 200 个用户同时访问,可能瞬间通过 200 个请求,在两个窗口的临界突刺,服务器可能就会有流量高峰危险。

优点:实现简单

缺点:突刺问题

2、滑动窗口限流

在单位时间内允许部分操作,不过这个单位时间是滑动的,需要指定一个滑动时间单位(时间片)。

**滑动窗口解决了固定窗口的突刺问题。**滑动窗口是持续滑动,逐时间片淘汰旧数据的过程,它不会像固定窗口一样在固定的时间点重置次数,而是持续淘汰旧数据,因此能更精确地控制任意连续内的请求速率。

比如,单位时间(窗口)是 1 小时,指定滑动单位是 1 分钟,那么在 59 分钟时它的限流窗口是 59 分1 小时 59 分,经过 2 分钟它的限流窗口就是 1 小时 01 分2 小时 01 分,这个时间段还是就只接收 100 个请求,如果有超过第 100 个的请求访问,超过的访问请求都会被拒绝掉;如果每 2 分钟都有 1 个旧的请求数据淘汰,经过 2 分钟这时只有 99 个数据就可以允许有 1 个新的请求进入。

优点:解决突刺问题

缺点:滑动窗口的限流效果与滑动单位有关,滑动单位小效果好,不过往往很难选到一个合适的滑动单位

3、漏桶算法限流(推荐)

请求桶大小固定,以固定的速率处理请求,当请求桶里面满了之后会拒绝请求。

漏桶算法限流的基本原理为:水(对应请求)从进水口进入到漏桶里,漏桶以一定的速度出水(请求放行),当水流入速度过大,桶内的总水量大于桶容量会直接溢出,请求被拒绝。

大致的漏桶限流规则如下:

  1. 进水口(对应客户端请求)以任意速率流入进入漏桶。
  2. 漏桶的容量是固定的,出水(放行)速率也是固定的。
  3. 漏桶容量是不变的,如果处理速度太慢,桶内水量会超出了桶的容量,则后面流入的水滴会溢出,表示请求拒绝。

以一定速率处理请求,有大量流量进入时,会发生溢出,从而限流保护服务可用(也就是削峰),不至于直接请求到服务器,缓冲压力(也就是缓冲)。

优点:一定程度上应对流量突刺,以恒定的速率处理请求,保证服务器安全

缺点:漏桶出口的速度固定,不能灵活的应对后端能力提升。比如,通过动态扩容,后端流量从 100QPS 提升到 1000QPS,漏桶没有办法。

4、令牌桶算法限流(推荐)

管理员提前生成一批令牌,比如每秒 10 个令牌,当用户要操作前,先去拿到一个令牌,有令牌的人就有资格执行操作、能同时执行操作,而拿不到令牌的就拒绝请求得等着。当然了令牌的数量也是有上限的,令牌的数量与时间和发放速率强相关,时间流逝的时间越长,会不断往桶里加入越多的令牌,如果令牌发放的速度比申请速度快,令牌桶会放满令牌,直到令牌占满整个令牌桶。

令牌桶的大小固定,令牌的产生速度固定可以自己设置,但是消耗令牌(即请求)速度不固定(可以应对一些某些时间请求过多的情况);每个请求都会从令牌桶中取出令牌,如果没有令牌则丢弃该次请求。

令牌桶限流大致的规则如下:

  1. 进水口按照某个速度,向桶中放入令牌。
  2. 令牌的容量是固定的,但是放行的速度不是固定的,只要桶中还有剩余令牌,一旦请求过来就能申请成功,然后放行。
  3. 如果令牌的发放速度,慢于请求到来速度,桶内就无牌可领,请求就会被拒绝。

优点:比漏桶更高效,可以方便地应对突发出口流量,能够并发处理同时的请求,并发性能会更高

缺点:还是时间单位选取的问题,令牌的产生速度、消耗令牌的速度对应时间选多少合适

限流实现

单机限流

使用谷歌提供的限流工具 Guava RateLimiter 第三方库实现,对单位时间的请求数量进行限制。

示例代码:

import com.google.common.util.concurrent.RateLimiter;

public static void main(String[] args) {
    // 每秒限流 10 个请求
    RateLimiter limiter = RateLimiter.create(10.0);
    while (true) {
        if (limiter.tryAcquire()) {
            // 处理请求
        } else {
            // 超过流量限制,需要做何处理
        }
    }
}

多机限流

  1. 使用操作 Redis 的工具库 Redisson 内置了一个限流工具,分布式限流实现,把用户的使用频率等数据放到一个集中的存储进行统计,这样无论用户的请求落到了哪台服务器,都以集中存储中的数据为准。
  2. 在网关层集中进行限流和统计,如 Sentinel、Spring Cloud Gateway

Redisson 限流实现

参考官方文档:https://github.com/redisson/redisson

首先引入 Redisson 依赖:

<!-- redisson-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>4.1.0</version>
</dependency>

还需要有 redis 的依赖和 application.yml 配置:

<!-- redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
# Redis 配置
redis:
  database: 0
  host: localhost
  port: 6379
  timeout: 5000
  # password: 123456

然后写个 RedissonConfig 配置类:

/**
 * Redisson 配置(初始化 RedissonClient 对象单例)
 */
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedissonConfig {
    private Integer database;
    private String host;
    private Integer port;
    // 如果你的 redis 默认没有设置密码则不用写
    private String password;

    @Bean
    public RedissonClient getRedissonClient() {
        // 1.创建配置对象
        Config config = new Config();
        config.useSingleServer()
                .setDatabase(database)
                .setAddress("redis://" + host + ":" + port);
        // 2.创建 Redisson 实例
        return Redisson.create(config);
    }
}

再写个通用的 RedissonManager 类,便于使用 redisson 限流功能:

查看源码发现 trySetRate 方法的参数用法在新版的 Redisson 中是被弃用的,现在推荐使用 RateIntervalUnit 的替代方案 org.redisson.api.RateType 和 Duration。

第一种是:

rateLimiter.trySetRate(RateType.OVERALL, 2, Duration.ofSeconds(1));

第二种是:

rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.valueOf("SECONDS"));

推荐使用第一种方式,因为:

  1. 使用 Duration 更符合 Java 8+ 的日期时间 API 设计理念
  2. 代码更简洁明了
  3. 这是 Redisson 官方推荐的新用法

因此,最终代码为

/**
 * 提供 RedissonLimiter 限流基础服务
 */
@Service
public class RedissonManager {
    @Resource
    private RedissonClient redissonClient;

    public void doRateLimit(String key) {
        // 创建一个限流器
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        // 设置限流器速率为每秒 2 个请求
        rateLimiter.trySetRate(RateType.OVERALL, 2, Duration.ofSeconds(1));
        // 每当一个请求过来,尝试获取一个令牌
        boolean canOp = rateLimiter.tryAcquire(1);
        if (!canOp) {
            //TOO_MANY_REQUEST_ERROR(42900, "请求过于频繁")
            throw new BusinessException(ErrorCode.TOO_MANY_REQUEST_ERROR, "操作太频繁,请稍后再试");
        }
    }
}

最后在 controller 中使用它:

/**
 * 获取当前登录用户
 *
 * @param request
 * @return
 */
@Override
public User getLoginUser(HttpServletRequest request) {
    // 先判断是否已登录
    // 用户登录态键 String USER_LOGIN_STATE = "user_login";
    Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
    User currentUser = (User) userObj;
    if (currentUser == null || currentUser.getId() == null) {
        //NOT_LOGIN_ERROR(40100, "未登录")
        throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "未登录");
    }
    // 从数据库查询(追求性能的话可以注释,直接走缓存)
    long userId = currentUser.getId();
    currentUser = this.getById(userId);
    if (currentUser == null) {
        throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "未登录");
    }
    return currentUser;
}
@Resource
private RedissonManager redissonManager;
User loginUser = userService.getLoginUser(request);
String id = String.valueOf(loginUser.getId());
redissonManager.doRateLimit("generateByAi_" + id);

目录

  1. 限流算法介绍
  2. 1、固定窗口限流
  3. 2、滑动窗口限流
  4. 3、漏桶算法限流(推荐)
  5. 4、令牌桶算法限流(推荐)
  6. 限流实现
  7. 单机限流
  8. 多机限流
  9. Redisson 限流实现
  10. Redis 配置
  11. password: 123456
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 国产大模型全景清单与行业应用趋势分析
  • DeerFlow 2.0 开源:打造真正的超级智能体架构
  • 鸿蒙 HarmonyOS 6 混合开发实战:ArkWeb 加载机制与 Cookie 管理
  • 从零开始折腾 Alas:碧蓝航线自动化部署与踩坑记录
  • OpenHarmony 使用 web_socket 实现跨平台 WebSocket 通信
  • Stable Diffusion WebUI 数据集标签编辑工具详解
  • 基于 LangChain 实现数据库问答机器人
  • C/C++ 动态规划:二维路径问题实战
  • 2026 春晚机器人行业观察:流量博弈与落地挑战
  • Spring Boot 日志全方位指南:最佳实践与配置详解
  • Python 学习指南:核心优势与应用场景解析
  • 零基础学习 AI:大模型技术原理与应用入门
  • 实战指南:如何设计去AI味的Prompt提升AIGC内容质量
  • C++手写AVL树:插入、旋转与自平衡验证
  • OpenClaw Gateway 服务运维:启动、停止与监控实践
  • Gitea 本地部署与常用 Git 命令实战指南
  • 基于 Java SSM 框架的线上学习网站设计与实现
  • FaceFusion AI 换脸工具本地部署与使用指南
  • Python 并发编程实战:多线程与多进程详解
  • Visual C++ 运行库故障诊断与修复指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online