当前位置: 首页 > news >正文

【设计题】如何实现限流器

实现限流器需根据场景(单机 / 分布式)选择合适的算法,业界主流方案基于令牌桶、漏桶、滑动窗口等核心思想,结合成熟工具或自研逻辑实现。以下是具体实现方案,涵盖单机和分布式场景,并参考业界主流实践(如 Guava、Redis 等)。

一、核心限流算法(业界基础)

算法核心思想优势劣势适用场景
固定窗口单位时间内(如 1 秒)限制请求数,超过则拒绝实现简单,内存占用低窗口边缘可能出现流量突增(如 59 秒和 0 秒各发 100 次)对精度要求不高的场景
滑动窗口将单位时间拆分为多个小窗口,滑动计算总请求数解决固定窗口的边缘问题,精度较高实现复杂,需存储各小窗口计数中等精度需求
漏桶算法请求先进入桶中,桶以固定速率流出,溢出则拒绝平滑流量输出,控制突发流量无法应对短时间内的合理流量峰值需严格控制输出速率的场景
令牌桶算法系统按固定速率生成令牌,请求需获取令牌才能通过,令牌可累积(应对突发流量)支持合理突发流量,灵活性高实现稍复杂大多数限流场景(推荐优先选)

二、单机限流器实现(参考 Guava)

1. 基于 Guava RateLimiter(令牌桶算法)

Guava 是 Java 领域最常用的单机限流工具,底层基于令牌桶算法,支持平滑突发流量和预热流量。

使用示例

import com.google.common.util.concurrent.RateLimiter;public class GuavaRateLimiterDemo {public static void main(String[] args) {// 1. 创建限流器:每秒生成10个令牌(QPS=10)RateLimiter limiter = RateLimiter.create(10.0);// 2. 尝试获取令牌(非阻塞)for (int i = 0; i < 15; i++) {boolean allowed = limiter.tryAcquire(); // 尝试获取1个令牌,无等待System.out.println("请求" + i + ":" + (allowed ? "通过" : "被限流"));}// 3. 阻塞获取令牌(适合必须处理的请求)limiter.acquire(2); // 获取2个令牌,阻塞等待System.out.println("获取2个令牌成功,处理请求");}
}

核心特性

  • create(10.0):每秒生成 10 个令牌,支持浮点型(如 2.5 表示每 400 毫秒 1 个令牌)。
  • tryAcquire(timeout, unit):带超时的非阻塞获取,超时未拿到令牌则拒绝。
  • acquire(n):阻塞等待获取 n 个令牌,适合必须执行的任务。
  • 预热模式:RateLimiter.create(10.0, 5, TimeUnit.SECONDS) 表示 5 秒内令牌生成速率从 0 逐渐提升到 10QPS,避免冷启动冲击。
2. 自研固定窗口限流器(简单场景)

适合对依赖 Guava 有顾虑的场景,基于 AtomicInteger 实现线程安全的固定窗口计数。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;public class FixedWindowLimiter {private final int maxRequests; // 窗口内最大请求数private final long windowMillis; // 窗口大小(毫秒)private final AtomicInteger count = new AtomicInteger(0); // 当前窗口请求数private volatile long windowStart; // 窗口开始时间public FixedWindowLimiter(int maxRequests, long windowMillis) {this.maxRequests = maxRequests;this.windowMillis = windowMillis;this.windowStart = System.currentTimeMillis();}public boolean tryAcquire() {long now = System.currentTimeMillis();// 1. 检查是否进入新窗口if (now - windowStart > windowMillis) {synchronized (this) { // 加锁确保窗口切换原子性if (now - windowStart > windowMillis) {count.set(0);windowStart = now;}}}// 2. 检查当前窗口请求数是否超过限制return count.incrementAndGet() <= maxRequests;}
}

使用场景:简单的接口限流,优点是实现轻量,缺点是存在窗口边缘流量突增问题。

三、分布式限流器实现(参考 Redis + Lua)

分布式系统中,单机限流无法控制集群整体流量,需基于 Redis 实现全局限流(业界主流方案)。

1. 基于 Redis + 滑动窗口算法(精确限流)

通过 Redis 的 ZSet 存储请求时间戳,滑动窗口内的请求数不超过阈值。

Lua 脚本(保证原子性)

-- 滑动窗口限流:key=限流标识,maxCount=窗口内最大请求数,windowMillis=窗口大小(毫秒)
local key = KEYS[1]
local maxCount = tonumber(ARGV[1])
local windowMillis = tonumber(ARGV[2])
local now = tonumber(ARGV[3])-- 1. 移除窗口外的请求(时间戳 < now - windowMillis)
redis.call('ZREMRANGEBYSCORE', key, 0, now - windowMillis)
-- 2. 统计当前窗口内的请求数
local currentCount = redis.call('ZCARD', key)
-- 3. 若未超过限制,添加当前请求时间戳
if currentCount < maxCount thenredis.call('ZADD', key, now, now .. ':' .. math.random()) -- 用随机数避免score冲突redis.call('EXPIRE', key, windowMillis / 1000 + 1) -- 设置过期时间,避免内存泄漏return 1 -- 允许请求
end
return 0 -- 拒绝请求

Java 调用示例

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.util.Collections;public class RedisSlidingWindowLimiter {private final StringRedisTemplate redisTemplate;private final DefaultRedisScript<Long> luaScript;public RedisSlidingWindowLimiter(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;// 加载Lua脚本this.luaScript = new DefaultRedisScript<>();this.luaScript.setScriptText(/* 上述Lua脚本字符串 */);this.luaScript.setResultType(Long.class);}public boolean allowRequest(String key, int maxCount, long windowMillis) {long now = System.currentTimeMillis();// 执行Lua脚本Long result = redisTemplate.execute(luaScript,Collections.singletonList(key),String.valueOf(maxCount),String.valueOf(windowMillis),String.valueOf(now));return result != null && result == 1;}
}

优势:精度高,解决固定窗口的边缘问题;适合集群环境全局限流。

2. 基于 Redis + 令牌桶算法(参考 Redisson)

Redisson 提供了分布式令牌桶实现 RSemaphore,但更灵活的方式是结合 Lua 脚本模拟令牌桶。

核心逻辑

  • 用 Redis 存储令牌桶的「当前令牌数」和「最后填充时间」。
  • 每次请求时,先根据当前时间和填充速率计算新增令牌数,再尝试消耗令牌。

适用场景:需要应对突发流量的分布式场景(如秒杀入口)。

四、业界成熟工具与框架集成

  1. Spring Cloud Gateway 限流:基于 Redis 实现滑动窗口限流,配置示例:

    yaml

    spring:cloud:gateway:routes:- id: service-routeuri: lb://servicepredicates:- Path=/api/**filters:- name: RequestRateLimiterargs:redis-rate-limiter.replenishRate: 10 # 令牌生成速率(QPS)redis-rate-limiter.burstCapacity: 20 # 令牌桶容量(最大突发流量)
    
  2. Sentinel 限流:阿里开源的流量控制框架,支持单机 / 分布式限流,基于滑动窗口算法,可通过控制台动态配置规则:

    // 初始化限流规则:资源名"order",QPS=10
    initFlowRules();
    // 限流保护的资源
    try (Entry entry = SphU.entry("order")) {// 业务逻辑
    } catch (BlockException e) {// 被限流,处理逻辑
    }
    

五、选型建议

  • 单机限流:优先用 Guava RateLimiter(简单、成熟),简单场景可自研固定窗口。
  • 分布式限流:首选 Redis + Lua 滑动窗口(精度高),或集成 Sentinel(功能全面,支持熔断降级)。
  • 高并发场景:令牌桶算法(支持突发流量)优于漏桶算法(严格限制速率)。

核心原则:根据业务对「精度」「突发流量容忍度」「分布式需求」选择合适方案,优先使用成熟工

http://www.dtcms.com/a/573382.html

相关文章:

  • 场外衍生品架构解析:TRS收益互换与场外个股期权的技术实现
  • 小程序定制开发实战:需求拆解、UI 设计与个性化功能落地流程
  • MATLAB基于变权理论和灰色云模型的海岛旅游地生态安全评价
  • 威联通nas 做网站长沙装修公司名单
  • 机器学习中的 fit()、transform() 与 fit_transform():原理、用法与最佳实践
  • 旅游景区网站建设的必要性织梦论坛
  • 【YashanDB认证】之三:用Docker制作YMP容器
  • 图文生视频的原理与应用
  • Java Spring Boot 项目 Docker 容器化部署教程
  • YOLOv8 模型 NMS 超时问题解决方案总结
  • 苏州网站设计公司有哪些行业网站导航
  • 福建外贸网站dw做网站注册页代码
  • VBA信息获取与处理专题五第三节:发送带附件的电子邮件
  • Linux上kafka部署和使用
  • 天河网站建设策划如何做阿里巴巴的网站
  • 网站建设自主开发的三种方式南充移动网站建设
  • 自动化测试用例的编写和管理
  • 头歌MySQL——数据库与表的基本操作
  • DUOATTENTION:结合检索与流式注意力机制的高效长上下文大语言模型推理方法
  • SAMWISE:为文本驱动的视频分割注入SAM2的智慧
  • Linux 进程状态:内核角度与应用层角度
  • A与非A、综合分析技巧
  • java之jvm堆内存占用问题
  • 江门网站制作设计网站地址栏图标文字
  • 做游戏网站多少钱网站做好了怎么上线
  • taro UI 的icon和自定义iconfont的icon冲突
  • 【开发】Git处理分支的指令
  • Linux 进程的写时拷贝(Copy-On-Write, COW)详解
  • git将克隆的目录作为普通文件夹上传
  • 集群网络技术1:RDMA和相关协议