Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >提升编程效率的利器: 解析Google Guava库之RateLimiter优雅限流(十)

提升编程效率的利器: 解析Google Guava库之RateLimiter优雅限流(十)

作者头像
公众号:码到三十五
修改于 2024-03-21 00:31:54
修改于 2024-03-21 00:31:54
1.9K00
代码可运行
举报
文章被收录于专栏:JAVA核心JAVA核心
运行总次数:0
代码可运行

一、RateLimiter的原理与特性

RateLimiter基于令牌桶算法(Token Bucket Algorithm)实现。该算法通过以恒定的速度向桶中添加令牌,并且每当有请求来时,需要从桶中取出一个或多个令牌才能继续执行。如果桶中没有足够的令牌,请求将被限流,即延迟处理或拒绝服务。

Guava的RateLimiter具有以下主要特性:

  1. 平滑突发流量:RateLimiter能够平滑地处理突发流量,确保系统不会因为瞬间的请求高峰而崩溃。
  2. 可配置的速率:开发者可以很容易地配置RateLimiter的令牌产生速率,以适应不同的应用场景和需求。
  3. 支持预热:RateLimiter允许在启动时进行预热,即在系统刚开始运行时逐渐增加令牌产生的速率,以避免冷启动问题。
  4. 线程安全:RateLimiter是线程安全的,可以在多线程环境中安全使用。

二、RateLimiter的功能与使用方法

RateLimiter提供了以下主要功能:

  • 创建RateLimiter实例:通过RateLimiter.create(double permitsPerSecond)方法可以创建一个RateLimiter实例,指定每秒生成的令牌数。
  • 获取令牌:通过acquire()方法可以获取一个令牌,如果桶中没有令牌,该方法会阻塞直到有令牌可用。此外,还提供了tryAcquire()方法用于非阻塞地尝试获取令牌。
  • 预热:通过RateLimiter.create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)方法可以创建一个带有预热期的RateLimiter实例。

使用RateLimiter的基本步骤如下:

  1. 创建RateLimiter实例,并指定每秒生成的令牌数。
  2. 在需要限流的地方调用acquire()tryAcquire()方法获取令牌。

    1. 如果成功获取到令牌,则继续处理请求;否则,根据业务逻辑进行相应的处理(如延迟、降级或返回错误)。

三、适用场景

RateLimiter适用于多种场景,包括但不限于:

  • API限流:保护后端服务免受恶意攻击或过量请求的损害。
  • 数据库访问限流:控制对数据库的并发访问量,防止数据库过载。
  • 网络爬虫控制:限制爬虫对目标网站的访问频率,以遵守robots.txt规则或减轻服务器负担。
  • 资源下载限速:控制客户端从服务器下载资源的速度,以防止带宽被占满。

四、使用案例

以下是一个RateLimiter使用案例,其中包含了限制API请求频率和用户登录次数的场景。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import com.google.common.util.concurrent.RateLimiter;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class AdvancedRateLimiterDemo {

    // 存储每个用户的API请求RateLimiter
    private static final Map<String, RateLimiter> apiRateLimiters = new HashMap<>();

    // 存储每个用户的登录尝试RateLimiter
    private static final Map<String, RateLimiter> loginRateLimiters = new HashMap<>();

    // 用于创建API请求RateLimiter的工厂方法
    public static RateLimiter createApiRateLimiter(double permitsPerSecond) {
        return RateLimiter.create(permitsPerSecond); // 每秒生成的令牌数
    }

    // 用于创建登录尝试RateLimiter的工厂方法
    public static RateLimiter createLoginRateLimiter(double permitsPerSecond) {
        return RateLimiter.create(permitsPerSecond);
    }

    // 获取或创建用户的API请求RateLimiter
    public static RateLimiter getApiRateLimiter(String userId) {
        return apiRateLimiters.computeIfAbsent(userId, k -> createApiRateLimiter(10.0)); // 每秒最多10个API请求
    }

    // 获取或创建用户的登录尝试RateLimiter
    public static RateLimiter getLoginRateLimiter(String userId) {
        return loginRateLimiters.computeIfAbsent(userId, k -> createLoginRateLimiter(1.0)); // 每秒最多1次登录尝试
    }

    // 模拟API请求
    public static boolean tryApiRequest(String userId) {
        RateLimiter rateLimiter = getApiRateLimiter(userId);
        if (!rateLimiter.tryAcquire()) {
            System.out.println("API请求过于频繁,请稍后再试。用户ID: " + userId);
            return false;
        }
        // 这里可以执行实际的API请求逻辑
        System.out.println("API请求成功处理。用户ID: " + userId);
        return true;
    }

    // 模拟用户登录尝试
    public static boolean tryLoginAttempt(String userId) {
        RateLimiter rateLimiter = getLoginRateLimiter(userId);
        if (!rateLimiter.tryAcquire()) {
            System.out.println("登录尝试过于频繁,请稍后再试。用户ID: " + userId);
            return false;
        }
        // 这里可以执行实际的登录验证逻辑
        System.out.println("登录尝试成功处理。用户ID: " + userId);
        return true;
    }

    public static void main(String[] args) {
        // 模拟同一用户连续发送多个API请求
        String apiUserId = "api-user-123";
        for (int i = 0; i < 15; i++) {
            new Thread(() -> tryApiRequest(apiUserId)).start();
            try {
                TimeUnit.MILLISECONDS.sleep(500); // 每隔500毫秒发送一个请求
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 模拟同一用户连续尝试登录
        String loginUserId = "login-user-456";
        for (int i = 0; i < 10; i++) {
            new Thread(() -> tryLoginAttempt(loginUserId)).start();
            try {
                TimeUnit.MILLISECONDS.sleep(200); // 每隔200毫秒尝试一次登录
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在这个示例中,我们定义了两个Map来分别存储用户的API请求RateLimiter和登录尝试RateLimiter。我们使用了computeIfAbsent方法来确保每个用户都拥有自己独立的RateLimiter实例。

tryApiRequest方法模拟了API请求的限流逻辑。如果用户请求过于频繁(即RateLimiter没有可用的令牌),则输出提示信息并返回false。否则,执行API请求的逻辑(在此处为打印语句)并返回true

类似地,tryLoginAttempt方法模拟了用户登录尝试的限流逻辑。如果用户登录尝试过于频繁,则同样输出提示信息并返回false。否则,执行登录验证的逻辑(在此处为打印语句)并返回true

main方法中,我们模拟了同一用户连续发送多个API请求和连续尝试登录的场景。由于RateLimiter的限制,部分请求和登录尝试将会因为频率过高而被拒绝。

五、实现机制

Guava的RateLimiter基于令牌桶算法实现,但进行了优化以支持平滑的突发流量处理。它内部使用了一个稳定的令牌产生速率和一个可配置的桶容量。当请求到达时,RateLimiter会根据当前的令牌数量和产生速率来决定是否立即处理请求、延迟处理请求还是拒绝请求。这种机制确保了系统在处理突发流量时能够保持稳定的性能。

六、最佳实践

在实际项目中运用RateLimiter时,以下是一些建议的最佳实践:

  1. 合理设置令牌产生速率:根据系统的实际处理能力和业务需求来设置合理的令牌产生速率。过高的速率可能导致系统过载,而过低的速率则可能限制系统的正常处理能力。
  2. 考虑预热期:对于需要快速响应的系统,可以设置一定的预热期来避免冷启动问题。预热期可以确保系统在刚开始运行时就能够以较高的速率处理请求。
  3. 结合降级策略使用:当系统面临过大的压力时,可以考虑结合降级策略使用RateLimiter。例如,当某个服务的请求量超过限流阈值时,可以将部分请求降级到备用服务或返回缓存结果。
  4. 监控与调优:在实际运行中,需要监控RateLimiter的表现并根据实际情况进行调优。可以通过监控令牌的产生速率、消耗速率以及请求的等待时间等指标来评估RateLimiter的性能和效果。
  5. 注意线程安全:虽然Guava的RateLimiter是线程安全的,但在使用过程中仍然需要注意线程安全的问题。特别是在多个线程共享同一个RateLimiter实例时,需要确保对令牌的获取和释放操作是原子的。

总之,Guava的RateLimiter是一个强大且灵活的组件,能够帮助开发者优雅地实现速率限制。通过深入了解其原理、特性、功能和使用方法,并结合实际项目的需求进行最佳实践的运用,我们可以更好地保护系统免受过量请求的损害并提高系统的稳定性和可伸缩性。


术因分享而日新,每获新知,喜溢心扉。 诚邀关注公众号 码到三十五 ,获取更多技术资料。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Google出品的限流术RateLimiter
限流 通过对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机。常用的限流算法有令牌桶和和漏桶,而Google开源项目Guava中的RateLimite
JavaQ
2018/04/04
2.6K0
Google出品的限流术RateLimiter
Guava RateLimiter单机实战指南
想象一下你是一位大厨,正在烹饪美味佳肴。突然之间,前来就餐的人潮如潮水般涌入,如果没有明智的限流策略,你可能会被厨房的热潮吞噬。现在,想象Guava的RateLimiter就像是一把魔法调味剂,帮助你在这个数字厨房中优雅地掌握料理节奏。本文将引领你走进这个美食的实战之旅。
一只牛博
2025/05/30
500
Guava RateLimiter单机实战指南
Guava RateLimiter限流源码解析和实例应用
Guava有两种限流模式,一种为稳定模式(SmoothBursty:令牌生成速度恒定),一种为渐进模式(SmoothWarmingUp:令牌生成速度缓慢提升直到维持在一个稳定值) 两种模式实现思路类似,主要区别在等待时间的计算上,本篇重点介绍SmoothBursty
算法之名
2019/08/20
8590
Guava RateLimiter限流源码解析和实例应用
使用Guava RateLimiter限流以及源码解析
首先通过RateLimiter.create(1);创建一个限流器,参数代表每秒生成的令牌数,通过limiter.acquire(i);来以阻塞的方式获取令牌,当然也可以通过tryAcquire(int permits, long timeout, TimeUnit unit)来设置等待超时时间的方式获取令牌,如果超timeout为0,则代表非阻塞,获取不到立即返回。
用户6182664
2020/05/08
1.3K0
使用Guava RateLimiter限流以及源码解析
超详细的Guava RateLimiter限流原理解析
 限流是保护高并发系统的三把利器之一,另外两个是缓存和降级。限流在很多场景中用来限制并发和请求量,比如说秒杀抢购,保护自身系统和下游系统不被巨型流量冲垮等。
程序员历小冰
2019/03/31
18.6K0
超详细的Guava RateLimiter限流原理解析
Guava RateLimiter详解以及源码分析
首先你需要明白限流的概念,在高并发、高流量的场景中,我们的系统有时候会通过限流的手段来防止自己的系统被外部的流量打挂,是一种自我保护措施。
用户7634691
2021/08/12
1.1K0
分布式环境下限流方案的实现redis RateLimiter Guava,Token Bucket, Leaky Bucket
对于web应用的限流,光看标题,似乎过于抽象,难以理解,那我们还是以具体的某一个应用场景来引入这个话题吧。在日常生活中,我们肯定收到过不少不少这样的短信,“双11约吗?,千款….”,“您有幸获得唱读卡,赶快戳链接…”。这种类型的短信是属于推广性质的短信。为什么我要说这个呢?听我慢慢道来。一般而言,对于推广营销类短信,它们针对某一群体(譬如注册会员)进行定点推送,有时这个群体的成员量比较大,甚至可以达到千万级别。因此相应的,发送推广短信的量也会增大。然而,要完成这些短信发送,我们是需要调用服务商的接口来完成的。倘若一次发送的量在200万条,而我们的服务商接口每秒能处理的短信发送量有限,只能达到200条每秒。那么这个时候就会产生问题了,我们如何能控制好程序发送短信时的速度昵?于是限流这个功能就得加上了
用户6182664
2020/05/11
6K0
限速神器RateLimiter源码解析
Tech 导读 在软件系统中,面对高并发的场景,经常需要通过限流来降低系统压力、保护系统不被压垮;另外在交易处理的场景中,也经常因下游要求或其他原因需控制处理速率。RateLimiter是谷歌开源的一款轻巧限流限速组件,简单实用,设计精妙,本文结合示例对其源码进行了相关分析解读,包括代码层级、处理流程、数据流转、计算逻辑等, 希望能够帮助大家了解和使用。
京东技术
2023/08/22
5680
限速神器RateLimiter源码解析
常见的限流解决方案
降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解决后再打开限流限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理
用户3467126
2019/10/29
4.4K0
常见的限流解决方案
实战限流(guava的RateLimiter)
程序员欣宸
2018/01/04
2.2K0
实战限流(guava的RateLimiter)
高并发之 API 接口,分布式,防刷限流,如何做?
降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解决后再打开
芋道源码
2020/04/17
1.9K0
高并发之 API 接口,分布式,防刷限流,如何做?
高并发利器-guava分流与缓存
有个场景,接口请求获取数据频繁,但数据改动量小,一般情况是先去redis取,没有则从数据库取,再放入redis,返回?为了加快系统的响应速度,我们可以在内存加一层,先查询内存缓存。没有则查询数据库/redis,再加入内存。
SAnBlog
2020/07/21
1.6K0
高并发利器-guava分流与缓存
Guava RateLimiter 实现 API 限流,这才是正确的姿势!
Guava提供的RateLimiter可以限制物理或逻辑资源的被访问速率,咋一听有点像java并发包下的Samephore,但是又不相同,RateLimiter控制的是速率,Samephore控制的是并发量。
JAVA葵花宝典
2021/10/20
10.6K0
限流原理解读之guava中的RateLimiter
RateLimiter.create做了两件事情创建Bursty对象和设置了速率,至次初始化过程结束
爬蜥
2019/07/08
1.7K0
SpringBoot整合RateLimiter实现限流
在和某学长炫耀在自己会用Redis+Lua实现滑动窗口限流时,他说现在都用RateLimiter,所以就我就想搞个Demo,但是度娘了一下,感觉我搜索到的博客有几个个人认为不太完善的地方,比如只贴了部分代码,没贴依赖。尤其是你用AOP实现的时候,其实依赖哪个还有有讲究的;还有一个问题就是大多都是基于AOP实现,拦截器实现也是一个不错的方式,所以此处用拦截器HandlerInterceptorAdapter实现。
CBeann
2023/12/25
4480
高并发系统设计之限流
当我们谈论Web应用或者服务,一个重要的话题就不能避免:「限流」。这是一种保护系统和维持服务稳定性的重要手段。
BookSea
2023/10/16
5580
高并发系统设计之限流
使用guava提供的ratelimiter令牌桶
在日常生活中,我们肯定收到过不少不少这样的短信,“京东最新优惠卷...”,“天猫送您...”。这种类型的短信是属于推广性质的短信。这种短信一般群发量会到千万级别。然而,要完成这些短信发送,我们是需要调用服务商的接口来完成的。倘若一次发送的量在200万条,而我们的服务商接口每秒能处理的短信发送量有限,只能达到200条每秒。那么这个时候就会产生问题了,我们如何能控制好程序发送短信时的速度昵?于是限流器就得用上了。
zhangheng
2020/04/29
2K0
使用guava提供的ratelimiter令牌桶
高并发系统设计之限流
当我们谈论Web应用或者服务,一个重要的话题就不能避免:「限流」。这是一种保护系统和维持服务稳定性的重要手段。
BookSea
2023/09/10
4090
高性能限流器 Guava RateLimiter
陈某的《Spring Cloud Alibaba实战项目》 视频教程已经录完了,涉及到Alibaba的各种中间件实战,戳这里--->Spring Cloud Alibaba 实战 视频专栏 开放订阅~
码猿技术专栏
2023/05/01
3420
高性能限流器 Guava RateLimiter
使用RateLimiter完成简单的大流量限流,抢购秒杀限流
RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率。
天涯泪小武
2019/01/17
1.1K0
相关推荐
Google出品的限流术RateLimiter
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验