我有一个命令行脚本,它执行 API 调用并使用结果更新数据库。
我对 API 提供程序设置了每秒 5 次 API 调用的限制。该脚本的执行时间超过 0.2 秒。
- 如果我按顺序运行该命令,它将运行得不够快,并且我每秒只会进行 1 或 2 次 API 调用。
- 如果我按顺序但同时从多个终端运行该命令,则可能会超过每秒 5 次调用的限制。
是否有办法编排线程,以便我的命令行脚本几乎每秒执行 5 次?
例如,某个程序将使用 5 或 10 个线程运行,并且如果前一个线程在不到 200 毫秒前执行了该脚本,则没有线程会执行该脚本。
答案1
在 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
仍会以相同的速率继续写入管道,直到管道变满(在默认管道大小为 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 次。
使用ksh93
(或使用,zsh
如果您的sleep
命令支持小数秒):
typeset -F SECONDS=0
n=0; while true; do
your-command &
sleep "$((++n * 0.2 - SECONDS))"
done
但这对并发数没有限制your-command
。
答案2
简单地说,如果您的命令持续时间少于 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
答案3
通过 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;
}
用它来了解如何创建线程:创建一个线程(这是我用来粘贴此代码的链接)
答案4
我已经使用 Stéphane Chazelas 的pv
解决方案一段时间了,但发现它在一段时间后(从几分钟到几个小时)随机(并且无声地)退出。 --编辑:原因是我的 PHP 脚本偶尔会因为超出最大执行时间而死掉,并以状态 255 退出。
所以我决定写一个简单的命令行工具这正是我所需要的。
实现我最初的目标很简单:
./parallel.phar 5 20 ./my-command-line-script
它几乎每秒启动 5 个命令,除非已经有 20 个并发进程,在这种情况下,它会跳过下一个执行,直到有一个槽可用。
此工具对状态 255 退出不敏感。