背景是任務派發系統,咱們咱們的任務落地在mysql, 而後有經過redis 的list 作隊列,由於任務是冪等的,因此,超時任務和失敗重試任務會從新入隊。這裏出現了一個狀況,就是咱們的任務,好比,視頻轉碼,是極其耗時的,任務出現重複入隊的狀況,這不是咱們所指望的,因此咱們須要對任務進行去重。python
該文章後續仍在不斷的更新修改中, 請移步到原文地址http://dmwan.ccmysql
由於redis 對lua 腳本是執行的,因此咱們的思路是 list + set ,來保證隊列去重。lua 腳本以下:golang
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 腳本的原子性,因此,能起到去重的做用。redis
在python 中調用的方式以下: sql
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 等其餘語言,使用方式同樣,沒什麼好說的。lua