使用 redis 的分布式锁

时间:2021-04-13 15:19:38

标签: python redis

处理锁持有者崩溃而不释放锁的情况,或者当锁持有者失败并永久持有锁时。我们使用 expire 为锁添加超时,如下所示:

# code snippet 1
def acquire_lock_with_timeout(
    conn, lockname, acquire_timeout=10, lock_timeout=10):
    identifier = str(uuid.uuid4())
    lock_timeout = int(math.ceil(lock_timeout))
    end = time.time() + acquire_timeout

    while time.time() < end:
        # if client happens to crash (between SETNX and EXPIRE)
        if conn.setnx(lockname, identifier): 
            conn.expire(lockname, lock_timeout)
            return identifier
        #  other client will check the expiration on the lock, and if it’s not set, set it.
        elif not conn.ttl(lockname):
            conn.expire(lockname, lock_timeout)
    time.sleep(.001)

return False

<块引用>

为了给我们的锁一个超时时间,我们将使用 EXPIRE 让 Redis 自动超时。放置 EXPIRE 的自然位置是紧接在锁之后 收购,我们会这样做。但是如果我们的客户端碰巧崩溃了(这是最糟糕的地方) 它对我们来说崩溃是在 SETNX 和 EXPIRE 之间),我们仍然希望锁最终 超时。为了处理这种情况,每当客户端无法获得锁时,客户端将 检查锁的到期时间,如果未设置,则设置它。因为客户要 检查和设置超时如果他们无法获得锁定,锁定将始终具有 超时,最终会过期,让其他客户端获得超时锁定。

所以这里的问题是:

我们是否可以像代码片段 2 一样使用 conn.setnxconn.expiremulti/exec 包装在 redis 事务中以确保这两个操作的原子性?它会像代码片段 1 一样工作吗?

# code snippet 2
def acquire_lock_with_timeout(
    conn, lockname, acquire_timeout=10, lock_timeout=10):
    identifier = str(uuid.uuid4())
    lock_timeout = int(math.ceil(lock_timeout))
    end = time.time() + acquire_timeout
    p = conn.pipeline()

    while time.time() < end:
        p.multi()
        if p.setnx(lockname, identifier): 
            p.expire(lockname, lock_timeout)
            if p.execute()[-1]:
                return identifier     

       time.sleep(.001)
return False

0 个答案:

没有答案