java 限制访问频率_java锁之RateLimiter(限制访问速率)

在日常生活中,我们肯定收到过不少不少这样的短信,“京东最新优惠卷…”,“天猫送您…”。这种类型的短信是属于推广性质的短信。这种短信一般群发量会到千万级别。然而,要完成这些短信发送,我们是需要调用服务商的接口来完成的。倘若一次发送的量在200万条,而我们的服务商接口每秒能处理的短信发送量有限,只能达到200条每秒。那么这个时候就会产生问题了,我们如何能控制好程序发送短信时的速度昵?于是限流器就得用上了。

RateLimiter经常用于限制对一些物理资源或者逻辑资源的访问速率。与Semaphore 相比,Semaphore 限制了并发访问的数量而不是使用速率。通过设置许可证的速率来定义RateLimiter。在默认配置下,许可证会在固定的速率下被分配,速率单位是每秒多少个许可证。为了确保维护配置的速率,许可会被平稳地分配,许可之间的延迟会做调整。可能存在配置一个拥有预热期的RateLimiter 的情况,在这段时间内,每秒分配的许可数会稳定地增长直到达到稳定的速率。

RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率。通常可应用于抢购限流防止冲垮系统;限制某接口、服务单位时间内的访问量,譬如一些第三方服务会对用户访问量进行限制;限制网速,单位时间内只允许上传下载多少字节等。

1、引入依赖

com.google.guava

guava

25.1-jre

2、代码实战一

每秒限制运行3个线程

public class RateLimiterTest {

public static void main(String[] args) throws InterruptedException {

//新建一个每秒限制3个的令牌桶 RateLimiter rateLimiter = RateLimiter.create(3.0);

ExecutorService executor = Executors.newFixedThreadPool(100);

for (int i = 0; i < 10; i++) {

executor.execute(new Runnable() {

@Override

public void run() {

//获取令牌桶中一个令牌,最多等待10秒 if (rateLimiter.tryAcquire(1, 10, TimeUnit.SECONDS)) {

System.out.println(Thread.currentThread().getName()+" "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

}

}

});

}

executor.shutdown();

}

}

运行结果:

pool-1-thread-1 2019-03-27 16:47:52

pool-1-thread-10 2019-03-27 16:47:53

pool-1-thread-9 2019-03-27 16:47:53

pool-1-thread-8 2019-03-27 16:47:53

pool-1-thread-7 2019-03-27 16:47:54

pool-1-thread-6 2019-03-27 16:47:54

pool-1-thread-5 2019-03-27 16:47:54

pool-1-thread-4 2019-03-27 16:47:55

pool-1-thread-3 2019-03-27 16:47:55

pool-1-thread-2 2019-03-27 16:47:55

注: public boolean tryAcquire(long timeout,TimeUnit unit)

从RateLimiter获取许可如果该许可可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待)。该方法等同于tryAcquire(1, timeout, unit)。

参数: timeout – 等待许可的最大时间,负数以0处理 unit – 参数timeout 的时间单位

返回:

true表示获取到许可,反之则是false

3、实战代码二

public class RateLimiterTest {

public static void main(String[] args) throws InterruptedException {

//每1s产生0.5个令牌,也就是说该接口2s只允许调用1次 RateLimiter rateLimiter = RateLimiter.create(0.5,1,TimeUnit.SECONDS);

ExecutorService executor = Executors.newFixedThreadPool(100);

for (int i = 0; i < 10; i++) {

executor.execute(new Runnable() {

@Override

public void run() {

//获取令牌桶中一个令牌,最多等待10秒 if (rateLimiter.tryAcquire(1, 10, TimeUnit.SECONDS)) {

System.out.println(Thread.currentThread().getName()+" "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

}else {

System.out.println("请求频繁");

}

}

});

}

executor.shutdown();

}

}

请求结果:

请求频繁

请求频繁

请求频繁

请求频繁

请求频繁

pool-1-thread-1 2019-03-27 17:18:01

pool-1-thread-10 2019-03-27 17:18:03

pool-1-thread-2 2019-03-27 17:18:05

pool-1-thread-9 2019-03-27 17:18:07

pool-1-thread-8 2019-03-27 17:18:09

从上面结果可以看出,接口限制为每2秒请求一次,那么同时来10个线程,需要20s全部处理完,但是rateLimiter.tryAcquire限制了10s内没有获取到令牌就抛出异常,所以结果中会有5个是请求频繁的。

4、方法摘要


版权声明:本文为weixin_42525387原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。