如何平均每秒运行5次命令?


21

我有一个命令行脚本,该脚本执行API调用并使用结果更新数据库。

我对API提供程序的每秒限制为5个API调用。该脚本需要花费超过0.2秒的时间来执行。

  • 如果我依次运行该命令,它将无法足够快地运行,并且我每秒只能进行1或2个API调用。
  • 如果我依次但在多个终端上同时运行命令,则我可能会超出5个调用/秒的限制。

是否可以通过某种方式来协调线程,以便我的命令行脚本几乎每秒精确执行5次?

例如,可以运行5个或10个线程的东西,如果前一个线程在200ms之前执行了脚本,则没有线程将执行该脚本。


所有答案都取决于您的脚本将按照调用顺序完成的假设。如果它们用完了就可以用在您的用例中吗?
科迪·古斯塔夫森

@CodyGustafson如果它们按顺序完成是完全可以接受的。我不认为至少在接受的答案中有这样的假设?
本杰明

如果您超过每秒的呼叫数量会怎样?如果API提供者无法控制,那么您不需要任何机制……对吗?
弗洛里斯

@Floris他们将返回错误消息,该错误消息将转换为SDK中的异常。首先,我怀疑如果每秒生成50条油门消息(您应该相应地对这些消息采取行动),API提供程序是否会满意,其次,我同时将API用于其他目的,所以我不想达到实际上更高的限制。
本杰明

Answers:


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行。

如果您的$cmds变慢了两个,导致达到20个限制,那么xargs它将停止读取,直到$cmd至少一个实例返回。pv仍将以相同的速率继续写入管道,直到管道变满为止(在Linux上,默认管道大小为64KiB将花费近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次。

使用ksh93(或zsh如果您的sleep命令支持小数秒):

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

但这并没有限制并发your-commands 的数量。


经过一点测试,该pv命令似乎正是我在寻找的东西,无法指望更好了!就在这行:yes | pv -qL10 | xargs -n1 -P20 sh -c "$cmd" sh,不是最后一个sh多余的吗?
本杰明

1
@Benjamin那第二个sh$0$cmd脚本中的。Shell也将其用于错误消息中。没有它,$0将是yyes,那么你会得到这样的错误信息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秒,则您每秒仅可以启动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

或者,您可以有5个独立运行的循环,最小运行时间为1秒。

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

相当不错的解决方案。我喜欢这样的事实,它很简单,每秒精确地运行5次,但是它具有同时启动5个命令(而不是每200ms)的缺点,并且可能缺乏一次最多运行n个线程的保障。 !
本杰明

@Benjamin我在第二个版本的循环中添加了200毫秒的睡眠时间。第二个版本一次不能运行超过5个cmd,因为我们只有每个开始5个,然后等待所有这些。
2013年

问题是,每秒启动的速度不能超过5;如果所有脚本突然花费超过1s的时间来执行,那么您就远远没有达到API限制。另外,如果您等待所有这些,单个阻止脚本会阻止所有其他脚本吗?
本杰明

@Benjamin因此,您可以运行5个独立的循环,每个循环的最小睡眠时间为1秒,请参见第3版。

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工具的东西!
本杰明

呀,计算器答案,这可能例如是使用多个工作线程之间共享一个令牌桶,但要求对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个并发进程,否则它将几乎每秒启动5个命令,在这种情况下,它将跳过下一个执行,直到某个插槽可用为止。

此工具对状态255退出不敏感。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.