Bootstrap

Python分布式锁:redis-py分布式锁的使用

背景

最近遇到一个问题,需要在python中开发一个分布式锁的控制,本来准备使用setNX等命令去实现一个Python的分布式锁,但是大家都知道要想实现一个好的分布式锁,要注意的事项太多了。然后发现redis-py已经封装了分布式锁,非常好,如果你需要有,希望本文对你有点帮助。

在这里插入图片描述

安装redis-py

pip install redis

redis-py的使用

参考我的这篇文档:如何使用Python操作Redis
在这里插入图片描述

redis-py分布式锁

redis-py框架提供了一个lock方法,用于实现分布式锁,lock函数:

def lock(self, name, timeout=None, sleep=0.1, blocking_timeout=None, lock_class=None, thread_local=True):

具体每个参数含义如下:

  • name: 锁的名字
  • timeout: 锁的生命周期。如果不设置锁就不会过期,直到被释放。默认不设置
  • sleep: 当获取锁阻塞时,尝试获取锁循环的间隔时间,默认是睡眠间隔0.1s尝试
  • blocking_timeout:当获取锁阻塞时,最长的等待时间,默认一直等待
  • lock_class: 强制执行指定的锁实现
  • thread_local:用来表示是否将token保存在线程本地。默认是保存在本地线程的,所以一个线程只能看到自己的token,而不能被另一个线程使用。比如有如下例子:
    0s: 线程1获取到锁my-lock,设置过期时间是5s,token是abc。
    1s:线程2尝试获取锁。
    5s:线程1还没有完成,redis释放了锁。同时线程2获取了锁,并设置token是xyz
    6s: 线程1执行完成,然后调用release()释放锁。如果token不是保存在本地,那么线程1将拿到token xyz,然后释放了线程2的锁
    在某些用例中,有必要禁用线程本地存储。
    例如,如果您有代码,其中一个线程获取一个锁,并将该锁实例传递给工作线程,以便稍后释放。
    如果在这种情况下未禁用线程本地存储,那么工作线程将看不到获取锁的线程设置的令牌。
    我们的假设是,这些情况并不常见,因此默认使用线程本地存储。
    通过创建lock时传入参数来控制lock的一些属性,比如获取锁的最长等待时间,持有锁的最长时间等。

使用方式1:

import redis

# redis 线程池
pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)
r = redis.Redis(connection_pool=pool)

# 创建一个锁
lock = r.lock('mylock')
try:
    # 获取锁
    lock.acquire()
    print('get lock')
except:
    pass
finally:
    # 释放锁
    lock.release()

使用方式2(推荐):

推荐使用wtih的方式,自动加锁和释放

with redis_service.normal_connection.lock(name='mylock', timeout=15, blocking=True, sleep=1):
    print('get lock')

lock的方法:

  • acquire:获取锁
  • release:释放锁,只能释放一次,释放多次会报错
  • owned:key是否被占有,拥有返回True
  • locked:锁是不是被另一个线程锁住,锁住返回True
acquire

如果锁已经被占有,再次accquire,获取失败,默认会阻塞

非阻塞使用

当设置了blocking=False时,表示拿不到锁时不阻塞,直接返回False

阻塞使用

当拿不到锁时可以设置阻塞的时长

# 阻塞等待5s
lock.acquire(blocking_timeout=5)
owned

owned表示key :mylock_one是不是被该锁 lock 作为关键字。被锁定返回True,没有锁定返回False

>>> lock = r.lock('mylock_one')
>>> 
>>> lock.owned()
False
>>> 
>>> lock.acquire()
True
>>> 
>>> lock.owned()
True
>>> 
locked

是用来看锁是不是被占用,占用返回True,没有被占用返回False

>>> lock = r.lock('mylock_two')
>>> 
>>> lock.locked()
False
>>> 
>>> lock.acquire()
True
>>> 
>>> lock.locked()
True
>>> 

总结

redis-py提供lock方法实现分布式锁,推荐使用with方式自动加锁和释放。

;