web-dev-qa-db-ja.com

Javaスロットル/スループット制御を備えたエグゼキュータ

Javaエクセキュータを探しています。たとえば、スロットル/スループット/ペーシングの制限を指定できます。たとえば、1秒間に100のタスクを処理できると言っても過言ではありません。送信され、キューに入れられて後で実行される必要がありますこれの主な目的は、外部APIまたはサーバーにアクセスするときに制限に達しないようにすることです。

Base Java(確認したので、疑問です)か、信頼できる他の場所(Apache Commonsなど)のどちらがこれを提供するのか、または自分で書く必要があるかどうか疑問に思っています。自分で書いてもかまいませんが、どこかに「標準」バージョンがある場合は、少なくとも最初にそれを確認したいと思います。

30
mrip

Guavasを見てください RateLimiter

レートリミッター。概念的には、レートリミッターは構成可能なレートで許可を配布します。パーミットが利用可能になるまで、それぞれのquire()は必要に応じてブロックし、それを取得します。取得後、許可証を発行する必要はありません。レートリミッターは、物理リソースまたは論理リソースへのアクセスレートを制限するためによく使用されます。これは、レートの代わりに同時アクセス数を制限するセマフォとは対照的です(ただし、同時実行性とレートは密接に関連していることに注意してください。たとえば、リトルの法則を参照してください)。

そのスレッドセーフですが、それでも@Beta。とにかく試してみる価値があるかもしれません。

レートリミッターに関して、Executorへの各呼び出しをラップする必要があります。よりクリーンなソリューションのために、ExecutorServiceのある種のラッパーを作成できます。

Javadocから:

 final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second"
  void submitTasks(List<Runnable> tasks, Executor executor) {
    for (Runnable task : tasks) {
      rateLimiter.acquire(); // may wait
      executor.execute(task);
    }
  }
27

Java Executorはそのような制限を提供していません。スレッドの量による制限のみです。これは、あなたが探しているものではありません。

一般に、エグゼキュータはそのようなアクションを制限するための間違った場所です。それは、スレッドが外部サーバーを呼び出そうとしているときでなければなりません。これを行うには、たとえば、スレッドが要求を送信する前にスレッドが待機する Semaphore を制限します。

呼び出しスレッド:

public void run() {
  // ...
  requestLimiter.acquire();
  connection.send();
  // ...
 }

同時に(単一の)セカンダリスレッドを定期的に(60秒ごとなど)取得したリソースを解放するようにスケジュールします。

 public void run() {
  // ...
  requestLimiter.drainPermits();  // make sure not more than max are released by draining the Semaphore empty
  requestLimiter.release(MAX_NUM_REQUESTS);
  // ...
 }
6
TwoThe

1秒間に100個のタスクを処理できると言うだけです-より多くのタスクが送信された場合、それらはキューに入れられて後で実行される必要があります

Executors.newFixedThreadPool(int limit)を調べる必要があります。これにより、同時に実行できるスレッドの数を制限できます。複数のスレッドを送信すると、それらはキューに入れられ、後で実行されます。

ExecutorService threadPool = Executors.newFixedThreadPool(100);
Future<?> result1 =  threadPool.submit(runnable1);
Future<?> result2 = threadPool.submit(runnable2);
Futurte<SomeClass> result3 = threadPool.submit(callable1);  
...  

上記のスニペットは、100個以下のスレッドを同時に実行できるExecutorServiceを使用する方法を示しています。

更新:
コメントを検討した後、これが私が思いついたものです(ちょっと愚かな)。実行されるスレッドを手動で追跡するのはどうですか?最初にそれらをArrayListに保存し、最後の1秒間にすでに実行されたスレッドの数に基づいてExecutorに送信するのはどうですか。
では、200のタスクが維持されているArrayListに送信されたとしましょう。反復して100をExecutorに追加できます。 2番目のパスでは、the Executorなどで完了したスレッドの数に基づいて、さらにスレッドを追加できます

4
Little Child

シナリオによっては、以前の応答の1つで提案されているように、ThreadPoolExecutorの基本的な機能でうまくいく場合があります。

ただし、スレッドプールが複数のクライアントで共有されていて、スロットルしたい場合は、それぞれの使用を制限し、1つのクライアントがすべてのスレッドを使用しないようにすると、BoundedExecutorが機能します。

詳細については、次の例をご覧ください。

http://jcip.net/listings/BoundedExecutor.Java

1
Joseph M. Dion

個人的には、このシナリオは非常に興味深いものでした。私の場合は、スロットルする興味深いフェーズは、古典的なプロデューサー/コンシューマーコンカレント理論のように、消費側のフェーズであることを強調したかったのです。これは、以前に提案された回答の一部とは逆です。つまり、送信スレッドをブロックするのではなく、レート(タスク/秒)ポリシーに基づいて消費スレッドをブロックします。したがって、キューに準備ができているタスクがある場合でも、スレッドの実行/消費は、スロットルポリシーを満たすための待機をブロックする可能性があります。

そうは言っても、Executors.newScheduledThreadPool(int corePoolSize)が良い候補だと思います。この方法では、executorの前に単純なキューが必要で(単純なLinkedBlockingQueueが適しています)、定期的なタスクをスケジュールして、キューから実際のタスクを選択します(ScheduledExecutorService.scheduleAtFixedRate)。したがって、これは簡単な解決策ではありませんが、前述のようにコンシューマーを抑制しようとする場合は、十分に機能するはずです。

0
Jaime Casero

Runnable内で制限できます。

public static Runnable throttle (Runnable realRunner, long delay) {
    Runnable throttleRunner = new Runnable() {
        // whether is waiting to run
        private boolean _isWaiting = false;
        // target time to run realRunner
        private long _timeToRun;
        // specified delay time to wait
        private long _delay = delay;
        // Runnable that has the real task to run
        private Runnable _realRunner = realRunner;
        @Override
        public void run() {
            // current time
            long now;
            synchronized (this) {
                // another thread is waiting, skip
                if (_isWaiting) return;
                now = System.currentTimeMillis();
                // update time to run
                // do not update it each time since
                // you do not want to postpone it unlimited
                _timeToRun = now+_delay;
                // set waiting status
                _isWaiting = true;
            }
            try {
                Thread.sleep(_timeToRun-now);

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // clear waiting status before run
                _isWaiting = false;
                // do the real task
                _realRunner.run();
            }
        }};
    return throttleRunner;
}

Java Thread Debounce and Throttle から取得

0
benbai123