1秒あたり平均5回でコマンドを実行する方法は?


21

API呼び出しを実行し、結果でデータベースを更新するコマンドラインスクリプトがあります。

APIプロバイダーでは1秒あたり5回のAPI呼び出しの制限があります。スクリプトの実行には0.2秒以上かかります。

  • コマンドを順番に実行すると、十分な速度で実行されず、1秒間に1つまたは2つのAPI呼び出ししか実行されません。
  • コマンドを連続して実行するが、複数の端末から同時に実行すると、5コール/秒の制限を超える可能性があります。

コマンドラインスクリプトが1秒間にほぼ5回実行されるようにスレッドを調整する方法がある場合

たとえば、5スレッドまたは10スレッドで実行されるもので、前のスレッドが200ミリ秒未満前に実行したスレッドはスクリプトを実行しません。


すべての答えは、スクリプトが呼び出された順に終了するという前提に依存します。ユースケースが順不同で終わった場合、それは受け入れられますか?
コーディ

@CodyGustafson順番がおかしくなっても問題ありません。少なくとも、受け入れられた答えにそのような仮定があるとは思わない?
ベンジャミン

1秒あたりの呼び出し数を超えるとどうなりますか?APIプロバイダーが調整する場合、最後にメカニズムは必要ありません...
フローリス

@Florisこれらは、SDKの例外に変換されるエラーメッセージを返します。まず第一に、毎秒50個のスロットルメッセージを生成するとAPIプロバイダーが満足することを疑います(そのようなメッセージに応じて行動することになっています)、第二に、同時にAPIを他の目的に使用しているので、実際にはわずかに高い制限に到達したくない。
ベンジャミン

回答:


25

GNUシステムでは、もしあればpv、次のことができます:

cmd='
   that command | to execute &&
     as shell code'

yes | pv -qL10 | xargs -n1 -P20 sh -c "$cmd" sh

-P20ほとんどの20で実行することである$cmdと同時に。

-L10 レートを1秒あたり10バイトに制限しているため、1秒あたり5行です。

あなたの場合$cmd、sは2遅くなると到達する20の限界を起こし、その後、xargs1つのまで読み取りを停止します$cmd少なくともリターンでインスタンス。pvパイプがいっぱいになるまで、同じ速度でパイプへの書き込みを続けます(デフォルトのパイプサイズが64KiBのLinuxでは、ほぼ2時間かかります)。

その時点で、pv書き込みを停止します。しかし、それでも、xargs読み取りを再開pvすると、全体で1秒あたり5行を維持するために、できるだけ早く送信する必要があるすべての行を追いつき、送信しようとします。

つまり、平均的な要件で1秒あたり5回の実行を20プロセスで実行できる限り、それを実行します。ただし、制限に達すると、新しいプロセスが開始される速度はpvのタイマーではなく、以前のcmdインスタンスが戻る速度によって決まります。たとえば、20が現在実行中で10秒間使用されていて、そのうちの10がすべて同時に終了することを決定した場合、10の新しいものが同時に開始されます。

例:

$ cmd='date +%T.%N; exec sleep 2'
$ yes | pv -qL10 | xargs -n1 -P20 sh -c "$cmd" sh
09:49:23.347013486
09:49:23.527446830
09:49:23.707591664
09:49:23.888182485
09:49:24.068257018
09:49:24.338570865
09:49:24.518963491
09:49:24.699206647
09:49:24.879722328
09:49:25.149988152
09:49:25.330095169

2つの実行間の遅延が常に正確に0.2秒であるとは限らない場合でも、平均で1秒あたり5回になります。

With ksh93(またはコマンドが小数秒をサポートするzsh場合sleep):

typeset -F SECONDS=0
n=0; while true; do
  your-command &
  sleep "$((++n * 0.2 - SECONDS))"
done

your-commandただし、同時sの数に制限はありません。


少しテストした後、pvコマンドは私が探していたものとまったく同じように思えます。次の行でyes | pv -qL10 | xargs -n1 -P20 sh -c "$cmd" sh、最後のsh冗長性はありませんか?
ベンジャミン

1
@Benjamin 2番目sh$0$cmdスクリプト内です。また、シェルによってエラーメッセージで使用されます。それがなければ、$0だろうyから、yesあなたのようなエラーメッセージを取得したいので、y: cannot execute cmdあなたも行うことができ...yes sh | pv -qL15 | xargs -n1 -P20 sh -c "$cmd"
ステファンChazelas

私はすべてを理解しやすい断片に分解するのに苦労しています、TBH!あなたの例では、これを最後に削除しましたsh。そして、私のテストでは、それを削除しても違いは見られません!
ベンジャミン

@ベンジャミン。重要ではありません。あなた$cmd$0(なぜそうするのか)そしてエラーメッセージのために使わない限り、それは違いを生じません。例えばで試してくださいcmd=/; 二ずにsh、あなたのようなものを参照したいy: 1: y: /: Permission deniedの代わりにsh: 1: sh: /: Permission denied
ステファンChazelas

私はあなたのソリューションに問題があります:それは数時間正常に動作し、その後、エラーなしで終了するだけです。これはパイプがいっぱいになり、予期しない副作用が発生する可能性がありますか?
ベンジャミン

4

簡単に言えば、コマンドの持続時間が1秒未満の場合、1秒間に5つのコマンドのみを開始できます。明らかに、これは非常にバースト的です。

while sleep 1
do    for i in {1..5}
      do mycmd &
      done
done

コマンドに1秒以上かかる場合があり、試してみてコマンドを分散したい場合

while :
do    for i in {0..4}
      do  sleep .$((i*2))
          mycmd &
      done
      sleep 1 &
      wait
done

または、最小1秒で独立して実行される5つの個別のループを使用できます。

for i in {1..5}
do    while :
      do   sleep 1 &
           mycmd &
           wait
      done &
      sleep .2
done

とてもいい解決策でもあります。私はそれがシンプルで毎秒正確に5回であるという事実が好きですが、(200ミリ秒ごとではなく)同時に5つのコマンドを開始するという欠点があり、一度に最大n個のスレッドを実行するという安全性が欠けている可能性があります!
ベンジャミン

@Benjamin 2番目のバージョンのループに200msのスリープを追加しました。この2番目のバージョンでは、開始するたびに5つしかコマンドを実行できないので、一度に5つ以上のコマンドを実行することはできません。
-meuh

問題は、1秒間に5個を超えて起動できないことです。すべてのスクリプトの実行に突然1秒以上かかる場合、APIの制限に達することはできません。さらに、それらすべてを待つと、1 つのブロックスクリプトが他のすべてをブロックしますか?
ベンジャミン

@Benjaminしたがって、5つの独立したループを実行できます。各ループの最小スリープは1秒です。第3バージョンを参照してください。
-meuh

2

Cプログラムでは、

たとえば、しばらくの間、0.2秒間スリープするスレッドを使用できます。

#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

pthread_t tid;

void* doSomeThing() {
    While(1){
         //execute my command
         sleep(0.2)
     } 
}

int main(void)
{
    int i = 0;
    int err;


    err = pthread_create(&(tid), NULL, &doSomeThing, NULL);
    if (err != 0)
        printf("\ncan't create thread :[%s]", strerror(err));
    else
        printf("\n Thread created successfully\n");



    return 0;
}

スレッドの作成方法を知るためにそれを使用します:スレッドを作成します(これはこのコードを貼り付けるために使用したリンクです)


答えてくれてありがとう。理想的には、Cプログラミングを含まず、既存のUnixツールのみを使用するものを探していました。
ベンジャミン

うん、例えばこのかもしれないとstackoverflowの答えは、複数のワーカースレッド間で共有トークンバケットを使用することが、Unix.SEに尋ねると、より「パワーユーザー」ではなく「プログラマ」アプローチのが望まれている:-)それでも、示唆してccいます既存のUnixツール。これは多くのコードではありません。
スティーブジェソップ

1

node.jsを使用すると、応答がコールバック関数を介して返されるため、応答がどのくらい長くかかっても、200ミリ秒ごとにbashスクリプトを実行する単一のスレッドを開始できます

var util = require('util')
exec = require('child_process').exec

setInterval(function(){
        child  = exec('fullpath to bash script',
                function (error, stdout, stderr) {
                console.log('stdout: ' + stdout);
                console.log('stderr: ' + stderr);
                if (error !== null) {
                        console.log('exec error: ' + error);
                }
        });
},200);

このjavascriptは200ミリ秒ごとに実行され、応答はコールバック関数を介して取得されますfunction (error, stdout, stderr)

このようにして、コマンドの実行がどの程度遅いか、または応答を待つ必要があるかに関係なく、毎秒5コールを超えないように制御できます。


私は、このソリューションのように:それは始まり、正確に一定の間隔で、毎秒5つのコマンドを。私が見ることができる唯一の欠点は、一度に最大n個のプロセスを実行するという安全対策がないことです!これが何か簡単に含めることができますか?私はnode.jsに詳しくありません。
ベンジャミン

0

私はpvしばらくの間、StéphaneChazelasをベースにしたソリューションを使用しましたが、しばらくしてから数分から数時間でランダムに(そして静かに)終了することがわかりました。- 編集:理由は、最大実行時間を超えたためにPHPスクリプトが時々停止し、ステータス255で終了することでした。

そこで、必要なことを正確に実行する簡単なコマンドラインツールを作成することにしました。

私の元の目標を達成するのは次のように簡単です:

./parallel.phar 5 20 ./my-command-line-script

すでに20の同時プロセスが存在しない限り、1秒あたりほぼ5つのコマンドを開始します。その場合、スロットが使用可能になるまで次の実行をスキップします。

このツールは、ステータス255終了の影響を受けません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.