Java节流

Java节流,java,throttling,Java,Throttling,如何结合使用ScheduledThreadPoolExecutor、ScheduledFuture和ExecutorCompletionService来限制接受可变参数的可调用的命令?在收到来自Callable命令的响应后,我需要基于前面提到的Callable命令的输出创建一个新的Callable命令。我还需要遵守每秒100次呼叫的阈值 您应该实施。在打电话之前,封锁直到你有了令牌。您可以在几十行Java中实现此算法。您可以使用信号量进行节流。您需要区分是A“每瞬间节流”(同时作业的上限)还是B

如何结合使用
ScheduledThreadPoolExecutor
ScheduledFuture
ExecutorCompletionService
来限制接受可变参数的
可调用的
命令?在收到来自
Callable
命令的响应后,我需要基于前面提到的
Callable
命令的输出创建一个新的
Callable
命令。我还需要遵守每秒100次呼叫的阈值

您应该实施。在打电话之前,封锁直到你有了令牌。您可以在几十行Java中实现此算法。

您可以使用信号量进行节流。您需要区分是A“每瞬间节流”(同时作业的上限)还是B“每间隔节流”(一个间隔内作业的上限)

A) 向上和向下计数一个信号灯就足够“每瞬间油门”。 例如

B) 对信号量进行倒计数,并定期将信号量重置为其初始值,对于“每间隔节流”来说就足够了

例如

一些评论:

1) 在生产系统中,最好使用带有超时的tryAcquire方法,而不是acquire方法,以避免实际的死锁


2) 如果处理多个作业,请在将作业提交给(计划)执行器服务之前致电aquire/tryAquire。否则,此时可能会有太多作业污染线程池的队列。

我建议使用代理,例如RabbitMQ。您可以将消费者的最大数量设置为100,并拥有一个生产者实例,该实例以每秒100条消息的速率发布消息

您可以找到在分布式系统中实现节流机制的三种方法的解释。您感兴趣的是使用RabbitMQ的分布式服务器。这一个设计用于限制任何给定时间的并发消息数量,比如说在任何给定时间最多100条。您需要对其进行修改,以便发布者每秒发布的消息不超过100条。在底部,您可以找到带有源代码的git存储库的url,但无论如何,我也在粘贴它

根据评论编辑:


首先使用java.util.Semaphore,它配置了它将处理的许可数量。每个线程都将尝试获取一个许可证,如果在释放一个许可证之前没有许可证,则将被阻止。第二个使用固定大小的ThreadPoolExecutor。执行器在任何给定时间最多将具有指定数量的工作线程。第三个使用RabbitMQ。并发使用者的最大数量将是工作线程的最大数量。git回购协议有更详细的英文解释。希望这对您有所帮助

如果您喜欢使用编写良好且有效的代码,您可以使用Apache的commons库(
org.Apache.commons.lang3.concurrent.TimedSemaphore
)中的
TimedSemaphore

例如,如果您需要在10秒内限制80次呼叫:

  private TimedSemaphore sem = new TimedSemaphore(10, TimeUnit.SECONDS, 80);
  [...]
  public void myMethod() throws InterruptedException {
    sem.acquire();
    // requests which need throttling
  }
  

最初我的想法是使用ScheduledThreadPoolExecutor的scheduleAtFixedRate操作,但这并没有提供足够的灵活性来更改Callable命令中的参数。确定。首先使用java.util.Semaphore,它配置了它将处理的许可数量。每个线程都将尝试获取一个许可证,如果在释放一个许可证之前没有许可证,则将被阻止。第二个使用固定大小的ThreadPoolExecutor。执行器在任何给定时间最多将具有指定数量的工作线程。第三个使用RabbitMQ。并发使用者的最大数量将是工作线程的最大数量。这本书有更详细的英文解释。希望如此helps@Imflores编辑成答案+1:)
10 : Job 1
9 : Job 2
8 : Job 3
7 : Job 5
7 : Job 4
...
9 : Job 87
10 : Job 90
9 : Job 89
10 : Job 91
9 : Job 92
8 : Job 93
7 : Job 94
6 : Job 95
5 : Job 98
4 : Job 97
3 : Job 96
2 : Job 100
1 : Job 99
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class ThrottlePerIntervallSample {
    private static final int JOBS_COUNT = 100;
    private static final int JOBS_THROTTLE_PER_INTERVALL = 10;
    private static final long INTERVALL_IN_UNITS = 1;
    private static final TimeUnit UNIT_OF_INTERVALL = TimeUnit.SECONDS;

    private static final Semaphore THROTTLE_PER_INTERVALL_SEMAPHORE = new Semaphore(
            JOBS_THROTTLE_PER_INTERVALL);

    private static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = Executors
            .newScheduledThreadPool(JOBS_THROTTLE_PER_INTERVALL + 1); 
    // plus one because the resetting of the semaphore must be possible! 

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

        SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate(()-> {
            THROTTLE_PER_INTERVALL_SEMAPHORE.drainPermits(); // remove permits from previous intervall
        THROTTLE_PER_INTERVALL_SEMAPHORE.release(JOBS_THROTTLE_PER_INTERVALL); // set permits for the next intervall
        }, INTERVALL_IN_UNITS, INTERVALL_IN_UNITS, UNIT_OF_INTERVALL);

        for (int i = 1; i <= JOBS_COUNT; i++) {
            THROTTLE_PER_INTERVALL_SEMAPHORE.acquire();
            final PrintJob printJob = new PrintJob(i);
            SCHEDULED_EXECUTOR_SERVICE.execute(printJob);
        }

        SCHEDULED_EXECUTOR_SERVICE.shutdown();
    }

    static class PrintJob implements Runnable {
        final int jobNumber;

        public PrintJob(int jobNumber) {
            super();
            this.jobNumber = jobNumber;
        }

        public void run() {

            try {
                Thread.sleep(50); // wait some time
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss:SSS");

            synchronized (System.out) {
                System.out.println(simpleDateFormat.format(new Date())
                        + " : Job " + jobNumber);
            }
        }
    }

}
00:42:29:253 : Job 9
00:42:29:255 : Job 2
00:42:29:255 : Job 6
00:42:29:255 : Job 5
00:42:29:255 : Job 10
00:42:29:256 : Job 7
00:42:29:256 : Job 3
00:42:29:256 : Job 1
00:42:29:256 : Job 8
00:42:29:257 : Job 4
00:42:30:140 : Job 11
...
00:42:37:142 : Job 90
00:42:38:140 : Job 91
00:42:38:140 : Job 92
00:42:38:141 : Job 99
00:42:38:141 : Job 93
00:42:38:141 : Job 94
00:42:38:142 : Job 98
00:42:38:142 : Job 96
00:42:38:142 : Job 95
00:42:38:143 : Job 100
00:42:38:143 : Job 97
  private TimedSemaphore sem = new TimedSemaphore(10, TimeUnit.SECONDS, 80);
  [...]
  public void myMethod() throws InterruptedException {
    sem.acquire();
    // requests which need throttling
  }