Como executar um comando em uma média de 5 vezes por segundo?

21

Eu tenho um script de linha de comando que executa uma chamada de API e atualiza um banco de dados com os resultados.

Eu tenho um limite de 5 chamadas de API por segundo com o provedor de API. O script leva mais de 0,2 segundos para ser executado.

  • Se eu executar o comando sequencialmente, ele não será executado com rapidez suficiente e só farei 1 ou 2 chamadas de API por segundo.
  • Se eu executar o comando em sequência, mas simultaneamente a partir de vários terminais, poderei exceder o limite de 5 chamadas / segundo.

Se existe uma maneira de orquestrar tópicos para que meu script de linha de comando seja executado quase exatamente 5 vezes por segundo?

Por exemplo, algo que seria executado com 5 ou 10 threads, e nenhum thread executaria o script se um thread anterior o executasse há menos de 200 ms.

    
por Benjamin 03.05.2016 / 10:29

5 respostas

0

Eu usei a solução pv baseada em Stéphane Chazelas por algum tempo, mas descobri que ela saía aleatoriamente (e silenciosamente) depois de algum tempo, de alguns minutos a algumas horas. - Edit: O motivo foi que meu script PHP morreu ocasionalmente devido a um tempo de execução máximo excedido, saindo com o status 255.

Então, decidi escrever uma simples ferramenta de linha de comando que faz exatamente o que eu preciso.

Alcançar meu objetivo original é tão simples quanto:

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

Ele inicia quase exatamente 5 comandos por segundo, a menos que já existam 20 processos concorrentes, caso em que ele pula a (s) próxima (s) execução (ões) até que um slot se torne disponível.

Esta ferramenta não é sensível a uma saída de status 255.

    
por 17.05.2016 / 16:56
24

Em um sistema GNU e se você tiver pv , você poderia fazer:

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

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

O -P20 é para executar no máximo 20 $cmd ao mesmo tempo.

-L10 limita a taxa a 10 bytes por segundo, portanto, 5 linhas por segundo.

Se o $cmd s se tornar dois lento e fizer com que o limite 20 seja atingido, xargs parará de ler até que pelo menos uma $cmd instância retorne. pv ainda continuará escrevendo para o pipe na mesma taxa, até que o pipe fique cheio (o que no Linux com um tamanho padrão de 64KiB levará quase 2 horas).

Nesse momento, pv deixará de ser escrito. Mas, mesmo assim, quando xargs retomar a leitura, pv tentará recuperar e enviar todas as linhas que deveria ter enviado antes o mais rápido possível, de modo a manter uma média global de 5 linhas por segundo.

O que isso significa é que, contanto que seja possível, com 20 processos atender a essa exigência média de 5 execuções por segundo, ele fará isso. No entanto, quando o limite é atingido, a taxa na qual novos processos são iniciados não será acionada pelo temporizador de pv, mas pela taxa na qual as instâncias de cmd anteriores retornam. Por exemplo, se 20 estão atualmente em execução e foram por 10 segundos, e 10 deles decidem terminar tudo ao mesmo tempo, então 10 novos serão iniciados de uma vez.

Exemplo:

$ 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

Em média, será 5 vezes por segundo, mesmo que o atraso entre duas execuções nem sempre seja exatamente de 0,2 segundos.

Com ksh93 (ou com zsh se o comando sleep suportar frações de segundo):

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

Isso não impõe limite ao número de your-command s simultâneos.

    
por 03.05.2016 / 10:48
4

Simplisticamente, se o seu comando durar menos de 1 segundo, você pode simplesmente iniciar 5 comandos por segundo. Obviamente, isso é muito rápido.

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

Se o seu comando demorar mais de um segundo e você quiser distribuir os comandos, tente

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

Como alternativa, você pode ter 5 loops separados que são executados independentemente, com um mínimo de 1 segundo.

for i in {1..5}
do    while :
      do   sleep 1 &
           mycmd &
           wait
      done &
      sleep .2
done
    
por 03.05.2016 / 11:13
2

Com um programa em C,

Você pode, por exemplo, usar um encadeamento que aguarde 0,2 segundos em um tempo

#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;
}

use-o para saber como criar um tópico: crie um tópico ( este é o link que usei para colar este código)

    
por 03.05.2016 / 10:48
1

Usando node.js , você pode iniciar um thread único que executa o script bash a cada 200 milissegundos, não importando o tempo que a resposta demore para retornar, porque a resposta é obtida uma função de retorno de chamada .

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);

Este javascript é executado a cada 200 milissegundos e a resposta é obtida através da função de retorno de chamada function (error, stdout, stderr) .

Desta forma, você pode controlar que nunca excede as 5 chamadas por segundo, independentemente de quão lenta ou rápida é a execução do comando ou quanto ele tem que esperar por uma resposta.

    
por 03.05.2016 / 11:45