背景是任务派发系统,我们我们的任务落地在mysql, 然后有通过redis 的list 做队列,因为任务是幂等的,所以,超时任务和失败重试任务会重新入队。这里出现了一个情况,就是我们的任务,比如,视频转码,是极其耗时的,任务出现重复入队的情况,这不是我们所期望的,所以我们需要对任务进行去重。
该文章后续仍在不断的更新修改中, 请移步到原文地址http://dmwan.cc
因为redis 对lua 脚本是执行的,所以我们的思路是 list + set ,来保证队列去重。lua 脚本如下:
SCRIPT_PUSH = `
local q = KEYS[1]
local q_set = KEYS[1] .. "_set"
local v = redis.call("SADD", q_set, ARGV[1])
if v == 1
then
return redis.call("RPUSH", q, ARGV[1]) and 1
else
return 0
end
`
SCRIPT_POP = `
local q = KEYS[1]
local q_set = KEYS[1] .. "_set"
local v = redis.call("LPOP", q)
if v ~= ""
then
redis.call("SREM", q_set, v)
end
return v
`
在push 任务之前,先看任务是否在set 中,在pop之后,立刻从set中删除,因为lua 脚本的原子性,所以,能起到去重的作用。
在python 中调用的方式如下:
import redis
SCRIPT_PUSH = '''
local q = KEYS[1]
local q_set = KEYS[1] .. "_set"
local v = redis.call("SADD", q_set, ARGV[1])
if v == 1
then
return redis.call("RPUSH", q, ARGV[1]) and 1
else
return 0
end
'''
SCRIPT_POP = '''
local q = KEYS[1]
local q_set = KEYS[1] .. "_set"
local v = redis.call("LPOP", q)
if v ~= ""
then
redis.call("SREM", q_set, v)
end
return v
'''
pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
r = redis.Redis(connection_pool=pool)
script1 = r.register_script(SCRIPT_PUSH)
script2 = r.register_script(SCRIPT_POP)
print r.get("mykey")
print script1( keys=["mykey"], args = [1,0] )
print r.get("mykey"), "ok"
print r.get("mykey")
print script2( keys=["mykey"], args = [1] )
print r.get("mykey")
golang 等其他语言,使用方式一样,没什么好说的。