Guava RateLimiter限流器使用示例
Guava中的RateLimiter可以限制单进程中某个方法的速率,本文主要介绍如何使用,实现原理请参考文档:推荐:超详细的Guava RateLimiter限流原理解析和推荐:RateLimiter 源码分析(Guava 和 Sentinel 实现)。
1 基于spring-mvc的controller测试限流
完整代码可参考:https://github.com/sxpujs/spring-cloud-examples/tree/master/rest-service
1.1 增加Maven依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
1.2 AccessLimitService 限流Service类
@Service
public class AccessLimitService {
// 每秒发出5个令牌
RateLimiter rateLimiter = RateLimiter.create(5.0);
/**
* 尝试获取令牌
*/
public boolean tryAcquire() {
return rateLimiter.tryAcquire();
}
}
1.3 控制器类
@RestController
@Slf4j
public class HelloController {
@Autowired
private AccessLimitService accessLimitService;
@RequestMapping("/access")
public String access() {
if (accessLimitService.tryAcquire()) {
log.info("start");
// 模拟业务执行500毫秒
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "access success [" + LocalDateTime.now() + "]";
} else {
//log.warn("限流");
return "access limit [" + LocalDateTime.now() + "]";
}
}
}
1.4 使用wrk工具模拟客户端发起多个请求
我们使用HTTP基准工具wrk来生成大量HTTP请求。在终端输入如下命令来测试:
wrk -t1 -c10 -d2s http://127.0.0.1:8080/access
服务端日志如下所示(稍做简化),可以看出前6行的执行时间是一样的,这是因为RateLimiter的默认实现SmoothBursty会缓存1秒的许可,在定义RateLimiter实例时,每秒5个许可,加上新占用的1个许可,一共有6个。从第7行开始,每0.2秒执行1次,符合预期。
2020-07-05 15:46:16.605 INFO --- [nio-8080-exec-2] HelloController : start
2020-07-05 15:46:16.605 INFO --- [nio-8080-exec-3] HelloController : start
2020-07-05 15:46:16.605 INFO --- [nio-8080-exec-7] HelloController : start
2020-07-05 15:46:16.605 INFO --- [nio-8080-exec-8] HelloController : start
2020-07-05 15:46:16.605 INFO --- [nio-8080-exec-9] HelloController : start
2020-07-05 15:46:16.605 INFO --- [nio-8080-exec-4] HelloController : start
2020-07-05 15:46:16.804 INFO --- [nio-8080-exec-1] HelloController : start
2020-07-05 15:46:17.005 INFO --- [io-8080-exec-11] HelloController : start
2020-07-05 15:46:17.204 INFO --- [nio-8080-exec-9] HelloController : start
2020-07-05 15:46:17.404 INFO --- [nio-8080-exec-5] HelloController : start
2020-07-05 15:46:17.604 INFO --- [nio-8080-exec-8] HelloController : start
2020-07-05 15:46:17.804 INFO --- [nio-8080-exec-2] HelloController : start
2020-07-05 15:46:18.004 INFO --- [nio-8080-exec-7] HelloController : start
2020-07-05 15:46:18.204 INFO --- [nio-8080-exec-6] HelloController : start
2020-07-05 15:46:18.404 INFO --- [nio-8080-exec-5] HelloController : start
2 基于单个类的main方法测试限流
package com.demo.guava;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
@Slf4j
public class RateLimiterDemo {
static void submitTasks1() {
ExecutorService pool = Executors.newFixedThreadPool(10);
RateLimiter rateLimiter = RateLimiter.create(5); // rate is "5 permits per second"
IntStream.range(0, 10).forEach(i -> pool.submit(() -> {
if (rateLimiter.tryAcquire()) {
try {
log.info("start");
Thread.sleep(500);
} catch (InterruptedException e) {
}
} else {
log.warn("限流");
}
}));
pool.shutdown();
/*
16:18:18.784 [pool-1-thread-1] INFO RateLimiterDemo - start
16:18:18.784 [pool-1-thread-7] WARN RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-2] WARN RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-4] WARN RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-5] WARN RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-6] WARN RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-9] WARN RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-3] WARN RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-10] WARN RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-8] WARN RateLimiterDemo - 限流
*/
}
static void submitTasks2() {
ExecutorService pool = Executors.newFixedThreadPool(10);
RateLimiter rateLimiter = RateLimiter.create(5); // rate is "5 permits per second"
IntStream.range(0, 10).forEach(i -> pool.submit(() -> {
rateLimiter.acquire();
log.info("start");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
pool.shutdown();
/*
16:18:56.030 [pool-1-thread-1] INFO RateLimiterDemo - start
16:18:56.227 [pool-1-thread-10] INFO RateLimiterDemo - start
16:18:56.428 [pool-1-thread-9] INFO RateLimiterDemo - start
16:18:56.627 [pool-1-thread-8] INFO RateLimiterDemo - start
16:18:56.827 [pool-1-thread-7] INFO RateLimiterDemo - start
16:18:57.028 [pool-1-thread-6] INFO RateLimiterDemo - start
16:18:57.226 [pool-1-thread-5] INFO RateLimiterDemo - start
16:18:57.426 [pool-1-thread-4] INFO RateLimiterDemo - start
16:18:57.629 [pool-1-thread-3] INFO RateLimiterDemo - start
16:18:57.826 [pool-1-thread-2] INFO RateLimiterDemo - start
*/
}
static void submitTasks3() {
RateLimiter r = RateLimiter.create(5);
log.info("start");
for (;;) {
log.info("get 1 tokens: " + r.acquire() + "s");
}
/*
16:15:46.310 [main] INFO RateLimiterDemo - start
16:15:46.315 [main] INFO RateLimiterDemo - get 1 tokens: 0.0s
16:15:46.513 [main] INFO RateLimiterDemo - get 1 tokens: 0.193752s
16:15:46.709 [main] INFO RateLimiterDemo - get 1 tokens: 0.194875s
16:15:46.911 [main] INFO RateLimiterDemo - get 1 tokens: 0.199033s
16:15:47.113 [main] INFO RateLimiterDemo - get 1 tokens: 0.197833s
16:15:47.312 [main] INFO RateLimiterDemo - get 1 tokens: 0.195898s
*/
}
static void submitTasks4() {
RateLimiter r = RateLimiter.create(5);
log.info("start");
for (;;) {
if (r.tryAcquire()) {
log.info("run");
}
}
/*
16:17:17.098 [main] INFO RateLimiterDemo - start
16:17:17.100 [main] INFO RateLimiterDemo - run
16:17:17.296 [main] INFO RateLimiterDemo - run
16:17:17.496 [main] INFO RateLimiterDemo - run
16:17:17.696 [main] INFO RateLimiterDemo - run
*/
}
public static void main(String[] args) throws InterruptedException {
//submitTasks1();
submitTasks2();
//submitTasks3();
//submitTasks4();
}
}
参考文档:
- 官方API:Class RateLimiter
- Baeldung: Quick Guide to the Guava RateLimiter
- Guava-RateLimiter详解
- 并发编程网:Guava官方文档-RateLimiter类
- 推荐:超详细的Guava RateLimiter限流原理解析
- 推荐:RateLimiter 源码分析(Guava 和 Sentinel 实现)
- 阿里巴巴Sentinel框架
Guava RateLimiter限流器使用示例的更多相关文章
- ☕【Java技术指南】「并发编程专题」针对于Guava RateLimiter限流器的入门到精通(含实战开发技巧)
并发编程的三剑客 在开发高并发系统时有三剑客:缓存.降级和限流. 缓存 缓存的目的是提升系统访问速度和增大系统处理容量. 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题 ...
- Java技术开发专题系列之【Guava RateLimiter】针对于限流器的入门到精通(针对于源码分析介绍)
Guava包中限流实现分析 RateLimiter 之前的文章中已经介绍了常用的限流算法,而google在Java领域中使用Guava包中的限流工具进行服务限流. 回顾使用案例 Google开源工具包 ...
- 超详细的Guava RateLimiter限流原理解析
超详细的Guava RateLimiter限流原理解析 mp.weixin.qq.com 点击上方“方志朋”,选择“置顶或者星标” 你的关注意义重大! 限流是保护高并发系统的三把利器之一,另外两个是 ...
- 常用限流算法与Guava RateLimiter源码解析
在分布式系统中,应对高并发访问时,缓存.限流.降级是保护系统正常运行的常用方法.当请求量突发暴涨时,如果不加以限制访问,则可能导致整个系统崩溃,服务不可用.同时有一些业务场景,比如短信验证码,或者其它 ...
- 使用Guava RateLimiter限流入门到深入
前言 在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流 缓存: 缓存的目的是提升系统访问速度和增大系统处理容量 降级: 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问 ...
- 限流 - Guava RateLimiter
2019独角兽企业重金招聘Python工程师标准>>> 限流 限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦并发访问/请求达到限制速率或者 ...
- Guava RateLimiter实现接口API限流
一.简介 Guava提供的RateLimiter可以限制物理或逻辑资源的被访问速率.RateLimit二的原理类似与令牌桶,它主要由许可发出的速率来定义,如果没有额外的配置,许可证将按每秒许可证规定的 ...
- 限流神器之-Guava RateLimiter 实战
前段时间,项目中需要对某些访问量较高的路径进行访问并发数控制,以及有些功能,比如Excel导出下载功能,数据量很大的情况下,用户不断的点击下载按钮,重复请求数据库,导致线上数据库挂掉.于是在这样的情况 ...
- Guava之FluentIterable使用示例
FluentIterable 是guava集合类中常用的一个类,主要用于过滤.转换集合中的数据:FluentIterable是一个抽象类,实现了Iterable接口,大多数方法都返回FluentIte ...
随机推荐
- Java实现第十届蓝桥杯质数
试题 D: 质数 本题总分:10 分 [问题描述] 我们知道第一个质数是 2.第二个质数是 3.第三个质数是 5--请你计算 第 2019 个质数是多少? [答案提交] 这是一道结果填空的题,你只需要 ...
- Java实现第九届蓝桥杯螺旋折线
螺旋折线 题目描述 如图p1.pgn所示的螺旋折线经过平面上所有整点恰好一次. 对于整点(X, Y),我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度. 例如dis(0 ...
- 二叉树的层次序列化和反序列化-----stringstream
string serialize(TreeNode* root) {//层序便利,将空的子节点也放入到字符串 ostringstream out; queue<TreeNode*> q; ...
- Spring zuul 快速入门实践 --看zuul如何进行服务转发
zuul 作为springCloud 的全家桶组件之一,有着不可或缺的分量.它作为一个普通java API网关,自有网关的好处: 避免将内部信息暴露给外部: 统一服务端应用入口: 为微服务添加额外的安 ...
- Linux内核配置编译及基本调试方法
一.Linux内核配置编译 1. 交叉编译设置:make ARCH=arm CROSS_COMPILE=arm-linux- 注:也可以直接修改顶层Makefile ARCH ?= arm CROSS ...
- java Exception 处理汇总
1.java.lang.Exception: No runnable methods 测试类,没有可以运行的方法 解决: 方法添加注释:@Test
- SQL Msg 18054, Level 16, State 1
今天接到一个看起来很简单的任务--修改数据库中的一项数据.听起来很简单吧. 在网上搜索了一下,很快就拼凑出了相应的 SQL 语句: UPDATE [suivi].[dbo].[numSerie]SET ...
- (三)MySQL基础查询(起别名、去重)
资料下载请前往:链接 补充内容: 1.数据库基本结构: 2.在sqlyog中将myemployees库导入数据库的方法: 右键root@localhost ->选择 执行SQL脚本->选定 ...
- group by <grouping sets(...) ><cube(...)>
GROUP BY GROUPING SETS() 后面将还会写学习 with cube, with rollup,以及将它们转换为标准的GROUP BY的子句GROUP SET(), CU ...
- java特性 JDK JRE JVM
1简单性 2可移植性性(跨平台) 3面向对象 4高性能 5分布式 6动态性 7多线程 8安全性JDK:java开发工具 . JRE:JDK:java运行环境 . JVM:JDK:java虚拟机