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

GUAVA 实现限流

目录

前言

1. 服务限流算法

1.1 漏桶算法

1.2 令牌桶算法

2. Guava限流

2.1 RateLimiter类

2.2  RateLimiter类-常用方法

2.3 Guava RateLimiter 优缺点对比

3. Guava 示例代码

3.1  基础阻塞式获取令牌

3.2  非阻塞式获取令牌(推荐用于Web场景)

3.3 带预热期的平滑限流

4、 面试回答总结


前言

刚毕业的时候,遇到了一次oom,领导安排给我分析排查解决。我最后实现的方式就是redis+guava。

现在开发中很少用guava限流了,原因是:

         单机限流,非分布式,这是最致命的缺点。 Guava的限流作用范围仅限于当前JVM实例。如果你有10台服务器,每台限流100 QPS,那么全局的总限流效果是 100 * 10 = 1000 QPS,而不是你想要的整个集群100 QPS。要实现集群限流,必须引入Redis等分布式协调器,但这就失去了Guava轻量级的优势。但是其限流思想还是很有学习意义的。

1. 服务限流算法

1.1 漏桶算法

桶算法的思想:

         是将请求放入一个有固定容量的桶中,然后以恒定的速率从桶中取出请求进行处理。如果桶满了,就丢弃新来的请求。这样可以保证请求的处理速率不超过设定的阈值,但是不能应对突发流量,因为突发流量会导致大量请求被丢弃

漏桶算法的优点:

1. 控制请求的传输速率:漏桶算法可以以固定的速率处理请求,保持请求的传输速率是均匀的,从而可以更好地控制流量

2. 对于下游系统的保护:漏桶算法在流量超过处理能力时,会丢弃请求,防止流量过载对下游系统造成冲击。

漏桶算法的缺点:

1. 无法应对瞬时突发流量:漏桶算法以固定速率处理请求,无法允许瞬时突发的高速访问

2.  有可能导致请求的延迟增加:当漏桶中没有足够的请求可以处理时,请求可能会排队等待,导致延迟增加。

1.2 令牌桶算法

令牌桶算法的思想:

         系统以恒定的速率向一个有固定容量的桶中放入令牌,然后每个请求都需要从桶中取出一个令牌才能被处理。如果桶中没有令牌,就暂时等待或者丢弃请求。这样可以保证请求的平均处理速率不超过设定的阈值,同时也允许一定程度的突发流量,因为当桶中有足够的令牌时,可以一次性处理多个请求

令牌桶算法的优点:

1. 瞬时突发:令牌桶算法允许请求以瞬间的高速率通过,只要令牌桶中有足够的令牌。

2. 平滑流量:令牌桶算法通过固定速率往令牌桶中添加令牌,可以平滑流量的突发。

3. 灵活性:令牌桶算法可以根据具体需求动态调整令牌的注入速率

令牌桶算法的缺点:

1.  对于处理速率的限制可能不够严格:当令牌产生速率很高时,可能会出现在短时间内处理过多请求的情况,这可能导致系统的负载增加。

2. Guava限流

2.1 RateLimiter类

        RateLimiter的核心思想是,它会以一定的速率生成令牌,然后在调用时根据令牌的可用性来控制操作的执行。如果没有令牌可用,RateLimiter可以通过阻塞线程来限制操作的执行速率,或者返回一个失败的结果。

  1. 令牌添加:系统以固定的时间间隔(如1/QPS秒)向桶中添加一个令牌。

  2. 令牌获取

    • 有令牌:直接获取,请求立即通过。

    • 无令牌

      • 可以等待:计算需要等待多久才能有下一个令牌,线程阻塞直到有可用令牌(平滑突发限流模式)。

      • 无法等待:直接返回获取失败(非阻塞模式)。

2.2  RateLimiter类-常用方法

2.3 Guava RateLimiter 优缺点对比
特性维度优点 (Advantages)缺点 (Disadvantages / Limitations)
部署与依赖极其轻量级,零外部依赖。仅是一个Jar包中的工具类,开箱即用,无需搭建任何额外的服务器或中间件。仅限于单机限流。无法进行集群级别的分布式限流,多台实例无法共享流量状态。这是其最致命的缺点。
性能基于内存,性能极高。所有计算在单JVM内完成,无网络开销,性能损耗极低(纳秒级别)。同步阻塞可能带来风险acquire()方法会阻塞线程,在Web容器中可能占满工作线程,影响整体吞吐量。
算法能力基于令牌桶算法,支持突发流量。允许消耗未来时间段的令牌,应对合理的瞬时流量高峰。功能单一,缺乏治理生态。只是一个“限流器”,而非“治理平台”。
高级特性提供平滑预热模式。系统启动时速率从低平滑过渡到设定值,保护冷系统,防止被流量击垮。无熔断降级能力。无法在服务调用失败率过高时自动熔断并执行降级逻辑。
易用性API 简单直观create(), acquire(), tryAcquire() 等方法非常容易理解和使用。无系统自适应保护。无法根据系统的CPU负载、并发线程数等实时指标动态调整限流阈值。
监控与配置-规则配置硬编码。限流规则写在代码中,修改必须发布应用,无法实现动态配置、实时生效。
适用场景-无实时监控与控制台。缺乏对QPS、拒绝数量等 metrics 的可视化监控和集中管理界面。
-不支持热点参数限流。无法对同一接口的不同参数(如不同商品ID)进行精细化区别限流。

3. Guava 示例代码

3.1  基础阻塞式获取令牌
import com.google.common.util.concurrent.RateLimiter;public class GuavaRateLimiterDemo {// 创建一个速率限制器,限制为每秒处理2个请求private static final RateLimiter rateLimiter = RateLimiter.create(2.0);public static void main(String[] args) {// 模拟10个请求for (int i = 0; i < 10; i++) {// 请求许可证(令牌),如果无法立即获得,则会阻塞直到可用double waitTime = rateLimiter.acquire();System.out.println("处理请求 " + i + ",等待了 " + waitTime + " 秒");handleRequest(i);}}private static void handleRequest(int index) {System.out.println(System.currentTimeMillis() + " -> 请求 " + index + " 正在执行...");}
}

输出分析
你会发现前两个请求几乎无需等待,因为桶中有令牌。从第三个请求开始,每个请求大约需要等待0.5秒(1秒/2个),因为速率被限制在了2 QPS。输出会显示等待时间。

处理请求 0,等待了 0.0 秒
1651234567890 -> 请求 0 正在执行...
处理请求 1,等待了 0.0 秒
1651234567891 -> 请求 1 正在执行...
处理请求 2,等待了 0.499 秒 // 开始等待
1651234568390 -> 请求 2 正在执行...
...
3.2  非阻塞式获取令牌(推荐用于Web场景)

这种方式更友好,不会阻塞业务线程。

import com.google.common.util.concurrent.RateLimiter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;@RestController
public class DemoController {// 限流器:每秒只允许处理10个请求private static final RateLimiter rateLimiter = RateLimiter.create(10.0);@GetMapping("/test")public String testEndpoint(HttpServletResponse response) throws IOException {// 尝试获取令牌,设置超时时间if (rateLimiter.tryAcquire()) { // 成功获取令牌,执行正常业务逻辑return "Success! Time: " + LocalDateTime.now();} else {// 获取失败,立即返回错误信息,避免阻塞response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); // 429response.getWriter().write("Too many requests, please try again later.");return null;}}
}
3.3 带预热期的平滑限流
public class WarmUpDemo {// 创建一个限流器:最终速率是每秒5个请求,预热期为10秒// 在这10秒内,速率会从低平滑地增加到5private static final RateLimiter rateLimiter = RateLimiter.create(5.0, 10, TimeUnit.SECONDS);public static void main(String[] args) {while (true) {double waitTime = rateLimiter.acquire();System.out.println("获取到令牌,等待时间: " + waitTime);// 在预热初期,waitTime会较长,随后逐渐变短并稳定。}}
}

4、 面试回答总结

面试官:“你怎么看Guava的RateLimiter?会在什么场景下使用它?”

你的回答

“Guava RateLimiter是一个基于令牌桶实现的、非常轻量高效的单机限流客户端库。它的优点是零依赖、性能极高、支持突发流量和预热模式

但是,它的局限性也非常明显:

第一,它是单机限流,无法做集群级别的流量控制。

 第二,功能比较单一,缺乏熔断降级、动态规则、系统自适应保护等高级治理功能。

因此,在我们的技术选型中:

  • 对于简单的单体应用测试环境、或者需要快速实现一个轻量级限流功能的场景,Guava是首选,因为它足够简单高效。

  • 但对于大规模的分布式微服务系统,我们一定会选择Sentinel这样的全功能治理平台。它能提供分布式集群流控、熔断降级、热点防护、实时监控和控制台动态配置等一系列能力,这才是保障复杂系统高可用的完整解决方案。

Guava像是一把出色的战术匕首,轻便锋利,适合小规模战斗或应急;而Sentinel则是一套完整的装甲武器系统,是为大规模战役准备的。”

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

相关文章:

  • GEO优化服务商赋能全球数字经济发展 技术创新引领行业新格局
  • Java—— 动态代理
  • 基于Python与Tkinter的校园点餐系统设计与实现
  • Spring Data Redis基础
  • [Vid-LLM] docs | 视频理解任务
  • Windows应急响应一般思路(三)
  • 第1.2节:早期AI发展(1950-1980)
  • 老字号:用 “老根” 熬活的 “新味道”
  • redis---string类型详解
  • 大模型四种常见安全问题与攻击案例
  • mysql的mvcc
  • 大语言模型应用开发——利用OpenAI函数与LangChain结合从文本构建知识图谱搭建RAG应用全流程
  • Redis全面详解:从配置入门到实战应用
  • 【前端debug调试】
  • 【Java SE】抽象类、接口与Object类
  • 从“一指禅”到盲打:如何系统提升电脑输入能力?
  • 25.深入对象
  • 联邦学习之----联邦批量归一化(FedBN)
  • 线程间Bug检测工具Canary
  • Python字符串
  • SOC估算方法-蜣螂优化算法结合极限学习
  • 1200 SCL学习笔记
  • 机器人控制基础:串级PID控制算法的参数如何整定?
  • 11.Shell脚本修炼手册---IF 条件语句的知识与实践
  • 无线数传模块保障智能立体车库多设备实时通信的可靠性
  • 二、BPMNJS简介
  • share logic in core or in example
  • 【typenum】 23 倒序存储的无符号整数(private.rs片段)
  • Linux mount 命令
  • PyInstaller将.py文件转为exe,执行文件在不同的电脑出现字体大小不一致问题原因分析及解决办法