用Redis实现分布式锁 与 实现任务队列

langzhiwu · · 1220 次点击
在下面这种情况下可能会有问题: A获取了锁,在expire之前A没有unlock,锁expire,B获取到了锁,A这时unlock,C获取到锁,这时B和C同时拥有锁 我觉得应该在每次获取锁时存一个uuid到redisKey: result, err := rlock.Redis_conn.Do("SETNX", redisKey, uuid) 在unlock时如下处理: func (rlock *RedisLock) Unlock(name string, uuid string) bool { redisKey := "kx:" + name if exist, _ := redis.Int(rlock.Redis_conn.Do("EXPIRE", redisKey, expire)); 0 == exist { //redisKey已经expire return true } //仍被本进程持有,或已经被其它进程持有 if _uuid, _ := redis.String(rlock.Redis_conn.Do("GET", redisKey)); _uuid != uuid { //已经被其它进程持有 return true } _, err := rlock.Redis_conn.Do("DEL", redisKey) if err != nil { return false } } LockedNames这个map似乎多余,并且在同一进程的多个goroutine使用同一lock的情况下这个map在按下面的顺序执行时会有问题: 1. goroutine A 执行`_, err := rlock.Redis_conn.Do("DEL", redisKey)` 2. goroutine B 执行`rlock.Redis_conn.Do("SETNX", redisKey, expireAt)`成功,然后执行`rlock.LockedNames[name] = expireAt` 3. goroutine A 执行`delete(rlock.LockedNames, name)` 4. goroutine B 执行Unlock时`if _, ok := rlock.LockedNames[name]; ok `条件不ok,`rlock.Redis_conn.Do("DEL", redisKey)`没执行
#4
更多评论
buscoop
纸上得来终觉浅,绝知此事要躬行!
拍砖star
#1
//ttl小于0 表示key上没有设置生存时间(key是不会不存在的,因为前面setnx会自动创建) //如果出现这种状况,那就是进程的某个实例setnx成功后 crash 导致紧跟着的expire没有被调用 //这时可以直接设置expire并把锁纳为己用 if ttl < 0 { rlock.Redis_conn.Do("EXPIRE", redisKey, expire) rlock.LockedNames[name] = expireAt return true } 这种情况下可能会有多个进程同时执行到这个条件里面,导致这些进程都获取到了锁 if timeout <= 0 || timeoutAt < time.Now().UnixNano()/1e6 { break } time.Sleep(time.Duration(waitIntervalUs)) `time.Now().UnixNano()/1e6`的单位是ms,而timeoutAt单位是秒; `time.Duration(waitIntervalUs)`是把waitIntervalUs当纳秒用了
#2