背景是任务派发系统,我们我们的任务落地在mysql, 然后有通过redis 的list 做队列,因为任务是幂等的,所以,超时任务和失败重试任务会重新入队。这里出现了一个情况,就是我们的任务,比如,视频转码,是极其耗时的,任务出现重复入队的情况,这不是我们所期望的,所以我们需要对任务进行去重。
该文章后续仍在不断的更新修改中, 请移步到原文地址
因为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 == 1then return redis.call("RPUSH", q, ARGV[1]) and 1else return 0end` 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)endreturn 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 == 1then return redis.call("RPUSH", q, ARGV[1]) and 1else return 0end''' 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)endreturn 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 等其他语言,使用方式一样,没什么好说的。