RateLimiter速率了解

RateLimiter

pom 复制代码
<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>22.0</version>
</dependency>

这个同名的类在nacos的jar包中也有出现。速率限制于java.util.concurrent.Semaphore功能相反,后者限制并发的访问数量,而不是速率(并发和速率密切相关)。见下面的Little定律

RateLimiter主要由颁发许可证的速率来定义。如果没有额外的配置,许可证将以固定的速率分配,以每秒的许可证定义。许可证将顺利分配,并调整各个许可证之间的延迟,以确保维持配置的费率。可以将RateLimiter配置为有一个预热期,在此期间,每秒发出的许可稳步增加,直到达到稳定的速率。

例如:任务每秒执行不超过2次

java 复制代码
final RateLimiter rateLimiter = RateLimiter.create(2.0); // 速率为"每秒2次许可"
void submitTasks(List<Runnable> tasks, Executor executor) {    
	for (Runnable task : tasks) {      
		rateLimiter.acquire(); // 等待
		executor.execute(task);    
	}  
}

例如:发送一个数据流,控制其速度在每秒为5kb,可以通过每个自己一个许可实现,并指定每秒5000个许可的速率

java 复制代码
final RateLimiter rateLimiter = RateLimiter.create(5000.0); // rate = 5000 permits per second  
void submitPacket(byte[] packet) {    
	rateLimiter.acquire(packet.length);    
	networkService.send(packet);  
}

for循环每秒打印一行,RateLimiter.create(1000);为什么会这样:

应为速率是指定为1000次/秒(类似频率),这个时候你一次要获取1000个凭证,因为这个会阻塞当前获取,所以你要算什么时候能获取到所有凭证的时间为:所需凭证数(1000)÷速率(1000次/秒) = 1秒

java 复制代码
public class TestRateLimiter {

    private static final Logger log = LoggerFactory.getLogger(PostExecutor.class);

    public static void main(String[] args) {
    	// 每秒最多允许1000个操作(每秒只能发送1000个凭证)
        RateLimiter limiter = RateLimiter.create(1000);
        log.info("start");
        for (int i = 0; i < 10; i++) {
        	// 获取1000个凭证,获取不到则等待
            limiter.acquire(1000);
            log.info("I: " + i);
        }
    }
}

在一定时间内获取对应的凭证 ,超时获取不到结束

java 复制代码
private static void tryAcquireTest1(RateLimiter limiter, int i){
	log.info("result: " + limiter.tryAcquire(20, 20000, TimeUnit.MILLISECONDS) + ", " + i);
}


这里的速率是2次每秒,可是我阻塞事件获取20个凭证,意味着10s执行一次,在多线程竞争的情况下,有一个线程多次false结束而且时间也没有达到20s(猜测是不是因为其他两个线程一共获取了40个凭证,意味着这20s之内不会再有新凭证了,所以他就不会非要等到20s才结束获取凭证,就提前结束了),启动两个线程,发现就会交替输出了,但是也出现了多次false的情况

java 复制代码
public class TestRateLimiter {

    private static final Logger log = LoggerFactory.getLogger(PostExecutor.class);

    public static void main(String[] args) {
        RateLimiter limiter = RateLimiter.create(2);
        log.info("start");
        
        Thread t1 = new Thread(()->{tryAcquireTest(limiter);});
        Thread t2 = new Thread(()->{tryAcquireTest(limiter);});
        Thread t3 = new Thread(()->{tryAcquireTest(limiter);});
        t1.start();
        t2.start();
        t3.start();

    }

    private static void tryAcquireTest(RateLimiter limiter){
        for (int i = 0; i < 10; i++) {
            tryAcquireTest1(limiter,i);
        }
    }

    private static void tryAcquireTest1(RateLimiter limiter, int i){
        log.info(Thread.currentThread().getName() + "result: " + limiter.tryAcquire(20, 20000, TimeUnit.MILLISECONDS) + ", " + i);
    }
}




相关推荐
名字还没想好☜13 小时前
Go 的 time.Ticker 陷阱:定时任务里被忽略的内存泄漏与正确关闭
java·数据库·golang·go·定时器
音符犹如代码14 小时前
后端视角看 EventBus:发布订阅总线的原理、场景与用法
java·spring boot·guava
前端炒粉14 小时前
手撕小汇总
java·前端·javascript
唐青枫14 小时前
Java Kafka 实战指南:从 Topic、分区到 Spring Boot 可靠消息处理
java
脱胎换骨-军哥15 小时前
C++零成本抽象理论深度拆解:现代C++如何在不牺牲性能的前提下提供高级语法封装
java·开发语言·c++
我是唐青枫15 小时前
Java Netty 实战指南:从 NIO 线程模型到 TCP 编解码和心跳机制
java·tcp/ip·nio
小白说大模型15 小时前
从向量嵌入到复杂 Agent:LLM、LangChain、LangGraph 完整科普
java·开发语言·人工智能·gpt·深度学习·langchain
风起洛阳@不良使16 小时前
springIOC创建对象的方式--spring容器中的注入
java·spring
zhanghaha131417 小时前
Python语言基础:4_数据类型转换
java·前端·python
Devin~Y17 小时前
互联网大厂Java面试实战:Spring Boot、MyBatis、Redis、Kafka、JWT、Spring Cloud 与 AI 场景追问
java·spring boot·redis·spring cloud·kafka·mybatis·spring security