《Redis应用实例》书摘(6):带密码保护功能的锁¶
一般情况下,除非程序出现bug或者操作错误,没有持有锁的客户端是不应该释放锁的,但为了彻底杜绝这一可能,我们可以给锁加上密码保护功能,使得锁只有在给定正确密码的情况下才会被释放。
代码清单 CODE_IDENTITY_LOCK 展示了根据带有密码保护功能的锁实现。
代码清单 CODE_IDENTITY_LOCK 带有密码保护功能的锁实现 identity_lock.py
from redis import WatchError
class IdentityLock:
def __init__(self, client, key):
self.client = client
self.key = key
def acquire(self, password):
"""
尝试获取一个带有密码保护功能的锁,
成功时返回True,失败时则返回False。
password参数用于设置上锁/解锁密码。
"""
return self.client.set(self.key, password, nx=True) is True
def release(self, password):
"""
根据给定的密码,尝试释放锁。
锁存在并且密码正确时返回True,
返回False则表示密码不正确或者锁已不存在。
"""
tx = self.client.pipeline()
try:
# 监视锁键以防它发生变化
tx.watch(self.key)
# 获取锁键存储的密码
lock_password = tx.get(self.key)
# 比对密码
if lock_password == password:
# 情况1:密码正确,尝试解锁
tx.multi()
tx.delete(self.key)
return tx.execute()[0]==1 # 返回删除结果
else:
# 情况2:密码不正确
tx.unwatch()
except WatchError:
# 尝试解锁时发现键已变化
pass
finally:
# 确保连接正确回归连接池,redis-py的要求
tx.reset()
# 密码不正确或者尝试解锁时失败
return False
正如前面所说,程序的acquire()方法会接受一个密码并在之后将其设置为键的值。
与此对应,release()方法在尝试解锁的时候,会先使用WATCH命令监视锁键,接着获取锁键的当前值并与给定密码进行对比:如果对比结果一致,那么程序就会以事务方式尝试删除锁键以释放锁。
以下是这个锁程序的使用方法:
>>> from redis import Redis
>>> from identity_lock import IdentityLock
>>> client = Redis(decode_responses=True)
>>> lock = IdentityLock(client, "Lock:10086")
>>> lock.acquire("top_secret") # 获取加密锁
True
>>> lock.release("wrong_password") # 密码错误,解锁失败
False
>>> lock.release("top_secret") # 密码正确,解锁成功
True