Java限流案例

wen java案例 1

本文目录导读:

Java限流案例

  1. 目录导读
  2. 为什么你的接口需要限流?
  3. 四大经典限流算法深度拆解
  4. Java生态限流工具选型
  5. 企业级限流实战案例
  6. 限流常见坑与面试高频问答
  7. 最后避坑指南

Java限流实战指南:从计数器到滑动窗口,彻底告别服务雪崩

目录导读

  1. 为什么你的接口需要限流? —— 理解限流的三重价值
  2. 四大经典限流算法深度拆解 —— 计数器/滑动窗口/漏桶/令牌桶
  3. Java生态限流工具选型 —— Guava RateLimiter vs Sentinel vs Resilience4j
  4. 企业级限流实战案例 —— 秒杀系统+外部API调用双场景落地
  5. 限流常见坑与面试高频问答 —— 避免“误伤”与“穿透”

为什么你的接口需要限流?

当你的订单接口每秒被调用1万次,而数据库只能承受2000QPS时,系统不会“优雅降级”,只会“崩溃雪崩”,限流的核心价值有三:

  • 保护自身:防止瞬时流量打垮数据库/线程池
  • 公平服务:确保每个用户获得基础服务能力(如支付接口单用户每秒最多5次)
  • 成本控制:避免外部API调用(如短信服务)产生巨额账单

真实案例:某电商平台曾因未做限流,大促期间Redis连接池被占满,导致全站卡死15分钟。


四大经典限流算法深度拆解

固定窗口计数器(最简单的入门实现)

// 核心实现:AtomicInteger + 时间窗口
private AtomicInteger count = new AtomicInteger(0);
private long windowStart = System.currentTimeMillis();
public boolean tryAcquire() {
    long now = System.currentTimeMillis();
    if (now - windowStart > 1000) {
        windowStart = now;
        count.set(0);
    }
    return count.incrementAndGet() <= MAX_PER_SECOND;
}

致命缺陷:窗口切换瞬间可能涌入双倍流量(如0.9s-1.1s期间可放行2倍请求)。

滑动窗口算法(解决临界突变)

将1秒拆成10个格子(每个100ms),每次请求滑动最近1秒统计,Spring Cloud Gateway默认使用此算法。

漏桶算法(强制匀速消费)

流量进桶,按固定速率漏出,适合保护下游敏感服务(如写入ES),但无法应对突发流量。

令牌桶算法(允许突发,最常用)

以固定速率生成令牌,桶内最多缓存N个,请求需获取令牌才可执行。Guava RateLimiter就是令牌桶实现


Java生态限流工具选型

工具 算法支持 分布式 学习成本 适用场景
Guava RateLimiter 令牌桶 单机接口防刷
Sentinel 滑动窗口+令牌桶 微服务集群限流降级
Resilience4j 滑动窗口 轻量级容错

选型建议:单机应用用Guava,Spring Cloud全家桶推荐Sentinel(控制台可视化规则)。


企业级限流实战案例

案例1:秒杀系统防刷限流(Sentinel + Redis)

@SentinelResource(value = "seckill", blockHandler = "handleBlocked")
public void doSeckill(Long userId) {
    // 秒杀逻辑
}
// 规则配置:每个用户每秒限流2次 + 全局限流5000QPS
private void initFlowRules() {
    FlowRule userRule = new FlowRule("seckill_user");
    userRule.setResource("seckill");
    userRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    userRule.setLimitApp(userId);  // 按参数限流
    userRule.setCount(2);
}

关键点:用用户ID做参数热点限流,防止脚本刷单。

案例2:外部第三方API调用的令牌桶保护

RateLimiter limiter = RateLimiter.create(20.0); // 每秒20个令牌
public String callSmsApi(String mobile) {
    if (!limiter.tryAcquire(1, 500, TimeUnit.MILLISECONDS)) {
        throw new BusinessException("短信发送太频繁,请稍后再试");
    }
    return httpClient.post(SMS_URL);
}

实测效果:对接某云短信服务,因限流避免了1300元/天的超量费用。


限流常见坑与面试高频问答

面试官最爱问的3个问题

Q1:令牌桶和漏桶的区别?什么时候选哪个?

  • 令牌桶:允许突发流量(最多突发桶容量个),平均速率恒定,适合:接口本身能应对短期毛刺。
  • 漏桶:强制平滑流量,不管外面多堵,出去始终匀速,适合:写数据库、调用付费API。

Q2:分布式限流怎么做?令牌桶能直接分布吗?

  • 不能,推荐方案:Sentinel(利用Redis+Nacos同步规则)或Redis+Lua原子脚本(INCR+EXPIRE实现滑动窗口)。
    -- Redis滑动窗口Lua伪代码
    local key = KEYS[1]
    local window = 1000  -- 窗口毫秒数
    local max = 5
    local current = redis.call('INCR', key)
    if current == 1 then redis.call('PEXPIRE', key, window) end
    if current > max then return 0 else return 1 end

Q3:限流和熔断的区别?

  • 限流:控制入口流量(不让请求进来)
  • 熔断:当自身节点出问题(响应慢/报错率高),主动断开调用(保护自己不再被拖垮)

最后避坑指南

  1. 别只做单机限流:线上多实例部署时,Guava会导致各实例流量不均,需改用Redis集中式限流。
  2. 超时时间要合理tryAcquire超时设置过短会误杀正常请求,建议500-1000ms。
  3. 限流后必须降级:返回友好提示(如“排队中”)比返回500错误更利于用户体验。

一句话总结:限流不是目的,让系统在极端流量下仍然可用才是终极目标。 从简单的计数器到Sentinel分布式治理,选择适合你业务阶段的方案,并确保每次拒绝都有明确的降级策略。

抱歉,评论功能暂时关闭!