Java 如何在一个时间段内限制线程数
我使用的服务在1秒内发出5个请求后开始阻止请求 在Spring中使用Java,我正在寻找一种对线程进行排队的方法,这种方法可以使多达5个线程在一秒钟内访问关键部分,并且任何其他线程都可以排队,一旦有带宽可以继续,就可以释放它们 目前,我尝试使用锁执行此操作,但它会导致线程始终等待1/5秒,即使我们不会在不睡眠的情况下达到每秒最大调用数Java 如何在一个时间段内限制线程数,java,multithreading,concurrency,critical-section,Java,Multithreading,Concurrency,Critical Section,我使用的服务在1秒内发出5个请求后开始阻止请求 在Spring中使用Java,我正在寻找一种对线程进行排队的方法,这种方法可以使多达5个线程在一秒钟内访问关键部分,并且任何其他线程都可以排队,一旦有带宽可以继续,就可以释放它们 目前,我尝试使用锁执行此操作,但它会导致线程始终等待1/5秒,即使我们不会在不睡眠的情况下达到每秒最大调用数 Lock l = new ReentrantLock(); try { l.lock(); //critical se
Lock l = new ReentrantLock();
try {
l.lock();
//critical section
} finally {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
l.unlock();
}
在这个实现中,我从不超过每秒5次,但在一切都准备好返回给用户之后,我也会导致响应延迟200毫秒
我需要一个只在需要延迟时延迟线程的解决方案。在这种情况下,一秒钟内的第6个以上呼叫应该延迟,但前5个呼叫不需要延迟。同样,调用6-11也可能同时进行。这种速率限制是微服务体系结构中的一个常见问题,因为它是解决级联故障这一更广泛问题的一部分。有很多库可以处理这个问题,其中一个最广泛使用的现代库叫做,它提供了一个
RateLimiter
实现。您可能想要与此非常接近的东西:
创建限制器:
RateLimiterConfig config=RateLimiterConfig.custom()
.limitRefreshPeriod(持续时间为秒(1))
.limitForPeriod(5)
.timeoutDuration(Duration.ofSeconds(4))//或失败前要等待多长时间
.build();
//创建注册表
RateLimiterRegistry RateLimiterRegistry=RateLimiterRegistry.of(配置);
//使用注册表
RateLimiter RateLimiter=rateLimiterRegistry
.rateLimiter(“someServiceLimiter”,配置);
使用它:
//装饰您对BackendService.doSomething()的调用
CheckedRunnable restrictedCall=费率限制器
.decorateCheckedRunnable(速率限制器、后端服务::doSomething);
//或者,可以使用注释:
@费率限制器(name=“SomeServiceLimitor”)
公共无效剂量测定法(){
//后端呼叫
}
我认为使用API解决它是最好的方法
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BulkheadSemaphore {
private Queue<Long> enterQueue = new LinkedList<>();
private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private Semaphore semaphore;
public BulkheadSemaphore(final Long timeLimit, final int concurrentThreadsLimit) {
this.semaphore = new Semaphore(concurrentThreadsLimit);
executor.scheduleAtFixedRate(() -> {
final Long now = now();
while (!enterQueue.isEmpty() && now - enterQueue.peek() >= timeLimit) {
enterQueue.poll();
semaphore.release();
}
}, timeLimit, 200, TimeUnit.MILLISECONDS);
}
private Long now() {
return System.currentTimeMillis();
}
public void acquire() {
try {
semaphore.acquire();
} catch (InterruptedException e) {
// todo: handle exception
}
}
public void release() {
semaphore.release();
}
}
import java.util.LinkedList;
导入java.util.Queue;
导入java.util.concurrent.*;
导入java.util.concurrent.locks.Lock;
导入java.util.concurrent.locks.ReentrantLock;
公共级大头机{
private Queue enterQueue=new LinkedList();
private ScheduledExecutorService executor=Executors.newSingleThreadScheduledExecutor();
专用信号量;
公用大容量主机(最终长时间限制、最终整数并发线程限制){
this.semaphore=新信号量(concurrentThreadsLimit);
执行者。日程安排固定日期(()->{
最后的长现在=现在();
而(!enterQueue.isEmpty()&&now-enterQueue.peek()>=timeLimit){
enterQueue.poll();
semaphore.release();
}
},timeLimit,200,TimeUnit.ms);
}
二等兵Long now(){
返回系统.currentTimeMillis();
}
公开无效获取(){
试一试{
semaphore.acquire();
}捕捉(中断异常e){
//todo:处理异常
}
}
公开无效释放(){
semaphore.release();
}
}
api非常简单:
bulkheademaphore.acqure()
bulkheademaphore.release()
- 此信号量为进入 很早以前的关键部分
- 它以一定的速率释放其许可(我将其设置为200ms,但可以更小)。它还保证,如果一个工作单元快速完成,下一个线程将能够启动一个新的工作单元李>
- 一些线程仍然会面临冗余等待,但这种情况并非每次都会发生,它们最多会花费200毫秒
timeLimit
设置为1.5秒,以符合您的1秒限制
另外,别忘了关闭executor服务试试Guava的RateLimitor。为什么要限制每秒,而不是一次只允许5次呢?@daniu当一次完成另一次时,需要让一个人进来。如果他们完成得很快,一秒钟内可能会执行5次以上。上面的关键代码部分使用第三方api,每秒调用5次后,该api将拒绝其他调用。一旦您决定要执行的操作,您需要释放锁。在执行实际工作时,无法保持锁定。我添加了依赖项,但对于@RateLimiter,我得到一个错误,它不是有效的注释。知道我遗漏了什么吗?注释RateLimitor和实现RateLimitor的名称相同。因此,如果在同一个文件中同时使用这两个文件,则需要指定其中一个文件的整个包。e、 g.
@io.github.resilience4j.ratelimiter.annotation.ratelimiter
谢谢。我能够编译它,但仍然能够让代码执行超过limitForPeriod。我在包含关键部分的同一个类中有这个逻辑。配置是否应该在其他地方?我在文档中看到他们将其放在yaml中,但这不是为我编译的,你使用的是io.github.resilience4j:resilience4j-spring-boot2
依赖关系吗?如果仍然有问题,您可能希望使用不带注释的方法。