초당 평균 5 번씩 명령을 실행하는 방법은 무엇입니까?


21

API 호출을 수행하고 결과로 데이터베이스를 업데이트하는 명령 줄 스크립트가 있습니다.

API 공급자와의 API 호출은 초당 5 회로 제한됩니다. 스크립트를 실행하는 데 0.2 초 이상 걸립니다.

  • 명령을 순차적으로 실행하면 명령이 충분히 빠르게 실행되지 않으며 초당 1 또는 2 개의 API 호출 만 수행합니다.
  • 명령을 순차적으로 실행하지만 여러 터미널에서 동시에 실행하면 5 호출 / 초 제한을 초과 할 수 있습니다.

명령 줄 스크립트가 초당 거의 정확히 5 번 실행되도록 스레드를 조정하는 방법이 있습니까?

예를 들어, 5 개 또는 10 개의 스레드로 실행되며 이전 스레드가 200ms 미만 전에 실행 한 스레드는 스크립트를 실행하지 않습니다.


모든 답변은 스크립트가 호출 된 순서대로 완료된다는 가정에 따라 다릅니다. 고장난 경우 사용 사례에 적합합니까?
코디 구스타프 슨

@CodyGustafson 순서가 잘못되면 완벽하게 허용됩니다. 적어도 받아 들여진 대답에 그런 가정이 있다고 생각하지 않습니다.
Benjamin

초당 통화 수를 초과하면 어떻게됩니까? API 제공 업체가 조절하는 경우 마지막에 메커니즘이 필요하지 않습니다.
Floris

@Floris SDK에서 예외로 번역되는 오류 메시지를 반환합니다. 우선 초당 50 개의 스로틀 메시지를 생성하면 (그러한 메시지에 따라 조치를 취해야 함) API 공급자가 행복 할 것이며, 둘째로 다른 목적으로 API를 동시에 사용하고 있기 때문에 실제로 약간 높은 한계에 도달하고 싶지 않습니다.
Benjamin

답변:


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 속도를 초당 10 바이트로 제한하므로 초당 5 행입니다.

귀하의 경우 $cmd의 느린 두되고 도달 할 20 한계가 발생하고 xargs한 때까지 읽기 중지됩니다 $cmd적어도 반환의 인스턴스입니다. pv파이프가 가득 찰 때까지 동일한 속도로 파이프에 계속 쓰기를 수행합니다 (기본 파이프 크기가 ​​64KiB 인 Linux에서는 거의 2 시간이 걸립니다).

그 시점에서 pv쓰기를 중지합니다. 그러나 그 후에도 xargs읽기를 다시 시작할 때 pv초당 평균 5 줄을 유지하기 위해 가능한 빨리 빨리 보내야하는 모든 줄을 잡아서 보내려고합니다.

즉, 20 개의 프로세스가 평균 요구 사항에서 초당 5 회 실행을 충족 할 수있는 한 가능합니다. 그러나 한계에 도달하면 새 프로세스가 시작되는 속도는 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

두 실행 사이의 지연이 항상 정확히 0.2 초가 아니더라도 평균적으로 초당 5 회입니다.

with ksh93(또는 명령이 소수 초를 지원 하는 zsh경우 sleep) :

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

your-command그러나 동시 수에는 제한이 없습니다 .


약간의 테스트를 마친 후에 pv명령은 내가 찾던 것과 정확히 같았으므로 더 나아질 수 없었습니다! 이 줄에 : yes | pv -qL10 | xargs -n1 -P20 sh -c "$cmd" sh, 마지막 sh중복이 아닌가?
Benjamin

1
그 두 번째 @Benjamin sh위한 $0당신의 $cmd스크립트. 쉘의 오류 메시지에도 사용됩니다. 그것 없이는 $0y에서 yes이 같은 오류 메시지를받을 것입니다, 그래서 y: cannot execute cmd당신은 또한 할 수 ...yes sh | pv -qL15 | xargs -n1 -P20 sh -c "$cmd"
스테판 Chazelas가

TBH! 모든 것을 이해할 수있는 부분으로 분해하려고 애 쓰고 있습니다. 귀하의 예에서, 당신은 이것을 마지막으로 제거했습니다 sh; 내 테스트에서 제거해도 아무런 차이가 없습니다!
Benjamin

@베냐민. 중요하지 않습니다. $cmd사용하지 않는 $0이유 (왜 그런가?)와 오류 메시지에 대해서만 달라집니다 . 예를 들어 cmd=/; 두 번째 sh가 없다면, y: 1: y: /: Permission denied대신에 다음과 같은 것을 보게 될 것 입니다sh: 1: sh: /: Permission denied
Stéphane Chazelas

귀하의 솔루션에 문제가 있습니다. 몇 시간 동안 정상적으로 작동 한 다음 어떤 시점에서 오류없이 종료됩니다. 파이프가 가득 차서 예상치 못한 부작용이 생길 수 있습니까?
Benjamin

4

간단히 말해, 명령이 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 번이라는 사실을 좋아하지만, 동시에 200ms 대신 5 개의 명령을 시작한다는 단점이 있으며 한 번에 최대 n 개의 스레드를 실행하는 보호 기능이 부족할 수 있습니다 !
Benjamin

@ Benjamin 두 번째 버전의 루프에서 200ms 수면을 추가했습니다. 이 두 번째 버전은 5를 시작할 때마다 한 번에 5 개 이상의 cmd를 실행할 수 없으며 모두 기다립니다.
meuh

문제는 초당 5 개 이상을 시작할 수 없다는 것입니다. 모든 스크립트가 갑자기 실행하는 데 1 초 이상 걸리는 경우 API 한계에 도달하지 못합니다. 또한, 당신이 그들 모두를 기다리는 경우, 하나의 차단 스크립트가 다른 모든 것을 차단합니까?
Benjamin

@Benjamin 따라서 각각 최소 1 초의 절전 모드로 5 개의 독립적 인 루프를 실행할 수 있습니다 (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 프로그래밍과 관련이 없지만 기존 유닉스 도구 만 사용하는 것을 이상적으로 찾고 있었지만 대답 해 주셔서 감사합니다!
Benjamin

네, 예를 들어,이 힘에 유래의 답은 여러 작업자 스레드간에 공유되는 토큰 버킷을 사용할 수 있지만 Unix.SE에 요구하는 것은 아니라 "프로그래머"접근 방식은 원입니다 :-)하지만보다 더 "전원 사용자"의 제안 cc입니다 기존 유닉스 도구이며 많은 코드가 아닙니다!
Steve Jessop

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

이 자바 스크립트는 200 밀리 초마다 실행되며 콜백 함수를 통해 응답을 function (error, stdout, stderr)받습니다.

이러한 방식으로 명령 실행 속도 또는 응답 속도 또는 응답 대기 시간과 상관없이 초당 5 회 호출을 초과하지 않도록 제어 할 수 있습니다.


이 솔루션이 마음에 듭니다. 일정한 간격으로 초당 정확히 5 개의 명령을 시작 합니다. 내가 볼 수있는 유일한 단점은 한 번에 최대 n 개의 프로세스를 실행하는 보호 기능이 없다는 것입니다! 이것이 당신이 쉽게 포함 할 수있는 것이라면? node.js에 익숙하지 않습니다.
Benjamin

0

나는 Stéphane Chazelas pv기반 솔루션을 얼마 동안 사용했지만 몇 분에서 몇 시간까지 어느 시간이 지나면 무작위로 (그리고 조용히) 빠져 나왔다는 것을 알았습니다. - 편집 : 그 이유는 최대 실행 시간이 초과되어 내 상태가 255 인 PHP 스크립트가 때때로 종료 되었기 때문입니다.

그래서 필요한 것을 정확하게 수행 하는 간단한 명령 줄 도구 를 작성하기로 결정했습니다 .

내 원래 목표를 달성하는 것은 다음과 같이 간단합니다.

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

이미 20 개의 동시 프로세스가없는 경우 초당 정확히 5 개의 명령을 시작합니다.이 경우 슬롯이 사용 가능해질 때까지 다음 실행을 건너 뜁니다.

이 도구는 상태 255 종료에 민감하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.