我有一个命令行脚本,该脚本执行API调用并使用结果更新数据库。
我对API提供程序的每秒限制为5个API调用。该脚本需要花费超过0.2秒的时间来执行。
- 如果我依次运行该命令,它将无法足够快地运行,并且我每秒只能进行1或2个API调用。
- 如果我依次但在多个终端上同时运行命令,则我可能会超出5个调用/秒的限制。
是否可以通过某种方式来协调线程,以便我的命令行脚本几乎每秒精确执行5次?
例如,可以运行5个或10个线程的东西,如果前一个线程在200ms之前执行了脚本,则没有线程将执行该脚本。
我有一个命令行脚本,该脚本执行API调用并使用结果更新数据库。
我对API提供程序的每秒限制为5个API调用。该脚本需要花费超过0.2秒的时间来执行。
是否可以通过某种方式来协调线程,以便我的命令行脚本几乎每秒精确执行5次?
例如,可以运行5个或10个线程的东西,如果前一个线程在200ms之前执行了脚本,则没有线程将执行该脚本。
Answers:
在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
s变慢了两个,导致达到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-command
s 的数量。
pv
命令似乎正是我在寻找的东西,无法指望更好了!就在这行:yes | pv -qL10 | xargs -n1 -P20 sh -c "$cmd" sh
,不是最后一个sh
多余的吗?
sh
是$0
您$cmd
脚本中的。Shell也将其用于错误消息中。没有它,$0
将是y
从yes
,那么你会得到这样的错误信息y: cannot execute cmd
......你也可以做yes sh | pv -qL15 | xargs -n1 -P20 sh -c "$cmd"
sh
;在测试中,将其删除时,看不到任何区别!
$cmd
确实使用$0
(为什么会这样)和出现错误消息,则只会有所不同。例如尝试cmd=/
;没有第二个sh
,你会看到类似y: 1: y: /: Permission denied
的替代sh: 1: sh: /: Permission denied
简而言之,如果您的命令持续不到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
用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;
}
用它来知道如何创建线程:创建线程(这是我粘贴此代码的链接)
cc
是一个现有的Unix工具,这不是很多代码!
使用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次调用,而与命令执行的速度或速度或等待响应的时间无关。