场景是 n 个耗时任务 A 需要执行,每个任务 A 之间互不影响,希望在 A 全部执行完成后开始执行某任务 B。
利用 sidekiq 来并行执行 A,当并发数 q << n 时,只需要将 n 个任务 A 和 一个任务 B 先后插入同一个 sidekiq 队列。若单个任务 A 最多耗时 t,则任务 B 执行前 sleep T,使得 T > t 即可。
这里利用了 sidekiq 队列先进先出的特点来进行并发控制。
后来想了一下,其实就是当并发数远小于任务数的时候,可以近似看做是顺序执行。Trick 在需要知道 sidekiq 实现的细节,同一队列任务确实是先进先出。
# at end of A worker
redis.incr "a-workers-done-couter"
# at beginng of B worker
while redis.get("a-workers-done-counter").to_i < N do
sleep 1
end
# at end of B worker
redis.del "a-workers-done-counter"
也许你的是 ok 的。我想,就上传图片吧,你可以很好的预测出时间来?((算上网络情况,服务器的情况),我没太明白“q << n”什么意思?1w 张图片,你就能保证一分钟后执行 B? sidekiq 并行执行任务和执行一个单个任务时间差不多?MRI ruby 是 green thread 吧,而且“队列先进先出”有啥关系?
@lithium4010 Ok 我理解你的意思了,确实和队列有关,往一个队列里放就保证前者执行后者后执行?这都是没有任何错误的情况吧,如果发生错误,需要重试呢?怎么布代码?hard code 1.minute ?那就更不靠谱了
#20 楼 @wpzero 对于发生错误的情况,需要保证 A 任务不重试,而是记录错误。sidekiq 使用 lpush 插入任务,brpop 弹出任务,所以一个队列中的任务是相对顺序执行的。当任务数远大于并发数是可以看做是顺序执行。 https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/client.rb#L199
这种思路本质是靠猜测前置任务的执行时间,影响因素太多且有潜在的时间浪费(任务失败,队列拥挤,Sidekiq 挂掉重启……)。
更稳定的做法是用 sidekiq-status 这类记录任务状态的插件,比如把前置任务 A 的 id 全部记下来,在 B 里检查前置任务的状态,再做针对性处理,比如全部完成且正确率到 90% 就处理,否则把自己 enqueue 到 n 秒之后。这个方法也能很容易的扩展成多级的任务链。
Sidekiq Pro 也有 batch 功能解决这类问题,我这种没钱用户没体验过。哪位有经验可以分享一下。
#26 楼 @darkbaby123 说的有道理,我这里给出的只是一个很粗糙的思路。对于具体的场景可以考虑的更精细。这里的本质应该是发现只需要猜测单一前置任务的执行时间,而不是所有前置任务的执行时间。
”若单个任务 A 最多耗时 t,则任务 B 执行前 sleep T,使得 T > t 即可“, 猜测 + Sleep,一个不靠谱,一个不确定,有的图片 10kb,有的图片 10M,你怎么估算时间