kevy4.0

队列与任务

把活交给 worker,
worker 挂了还能把活拿回来

关系型数据库里的队列表,本质是一套加了额外步骤的加锁约定。kevy 有两种真正的队列:丢一个任务还能承受的时候,用 list;承受不了的时候,用带消费组的 stream

两种选哪种

用 list:任务重做一遍成本很低,worker 也不太可能干到一半挂掉——发一封邮件、预热一个缓存、让一条 CDN 路径失效。BRPOP 会一直阻塞到有活为止,所以 worker 不必轮询。

用 stream:任务绝对不能丢。消费组会把每条消息恰好交给一个 worker,并且记住自己交过。如果这个 worker 在确认之前挂了,消息会留在 pending 列表里,另一个 worker 可以把它认领走——stream 存在的全部理由就是这个,它也正是一个队列和一份侥幸之间的差别。

怎么做——用 list,做可以重来的活

worker 直接阻塞。没有轮询循环,没有 sleep,也没有惊群。
# producer
LPUSH jobs:email '{"to":"ada@example.com","tpl":"welcome"}'

# worker — blocks until there is something, up to 30 seconds
BRPOP jobs:email 30
-> 1) "jobs:email"
   2) "{\"to\":\"ada@example.com\",\"tpl\":\"welcome\"}"

# a delayed job: the score is when it is due.
# ZPOPMIN.BELOW is kevy's own — it takes only what is actually due,
# and stops at the first job that is not.
ZADD jobs:due 1783875499 '{"id":"j-91"}'
ZPOPMIN.BELOW jobs:due 1783875500
-> 1) the job payload
   2) 1783875499

怎么做——用 stream,做丢不起的活

消息在有 worker 确认之前,一直是 pending 的。挂掉的 worker 手上那份活,可以被重新认领。
# once, at setup
XGROUP CREATE jobs:pay g1 $ MKSTREAM

# producer
XADD jobs:pay * order 4410 amount 8400

# worker: read, then work, then acknowledge
XREADGROUP GROUP g1 worker-3 COUNT 1 BLOCK 5000 STREAMS jobs:pay >
XACK jobs:pay g1 1783875499458-0

# the worker died before XACK. another one takes over:
XAUTOCLAIM jobs:pay g1 worker-7 60000 0-0
# claims anything idle for more than 60 s

# what is still outstanding, and who has it
XPENDING jobs:pay g1
代价是什么

stream 不是免费的。MAXLEN 裁剪会重算整条流的权重,复杂度是整条流的 O(N)——所以要按计划裁剪,不要每次 XADD 都裁。XREADGROUPCOUNT 限制的是交到你手上的条数,不是扫过的条数:整条尚未投递的尾巴会先被物化出来。另外在多 shard 的服务端上,BLPOP 跨多个 key 时并不遵守 Redis 那种严格的从左到右优先级——连接自己所在 shard 上的 key 会先被服务。这些都在命令参考里逐条写着。

接下来