这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
分布式锁实现方式
目前主流的分布式锁实现方式有以下几种:
- 基于数据库来实现,如 mysql
- 基于缓存来实现,如 redis
- 基于 zookeeper 来实现
我们今天主要写的是基于redis的分布式锁
相对于基于数据库实现分布式锁的方案来说,基于缓存来实现在性能方面会表现的更好一点,存取速度快很多。而且很多缓存是可以集群部署的,可以解决单点问题。
以下是django 在单点登录过程中 基于redis实现的分布式锁的实现
django 的 setting 配置 redis
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
# 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
'TIMEOUT': 300,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
# "PASSWORD": "yoursecret",
},
},
}
利用redis做分布式锁:
from django.core.cache import cache
# 分布式锁实现
class CacheLock(object):
def __init__(self, expires=60, wait_timeout=10):
self.cache = cache
self.expires = expires # 函数执行超时时间
self.wait_timeout = wait_timeout # 拿锁等待超时时间
def get_lock(self, lock_key):
# 获取cache锁
wait_timeout = self.wait_timeout
identifier = uuid.uuid4()
while wait_timeout >= 0:
if self.cache.add(lock_key, identifier, self.expires):
return identifier
wait_timeout -= 1
time.sleep(0.02)
def release_lock(self, lock_key, identifier):
# 释放cache锁
lock_value = self.cache.get(lock_key)
if lock_value == identifier:
self.cache.delete(lock_key)
def lock(cache_lock):
def my_decorator(func):
def wrapper(*args, **kwargs):
lock_key = 'bk_monitor:lock:{}'.format(args[1]) # 具体的lock_key要根据调用时传的参数而定
identifier = cache_lock.get_lock(lock_key) # 获取锁
try:
return func(*args, **kwargs)
finally:
cache_lock.release_lock(lock_key, identifier)
return wrapper
return my_decorator
django 实现用户登录
from django.shortcuts import redirect
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import login as auth_login, authenticate
from app.models import Users # 用户信息表
@lock(CacheLock())
def login(request):
if request.user.is_authenticated():
return redirect('/index/')
else:
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
authenticated_user = authenticate(username=username, password=password)
if authenticated_user:
user_obj = Users.objects.filter(userid=authenticated_user) # 找到登录的user对象
is_session_key = user_obj.first().session_key # 获取登录对象的session_key
if is_session_key: # 用户已登录
request.session.delete(is_session_key) # 删除登录前面登录用户的session_key
auth_login(request, authenticated_user) # 用户信息存入session
user_obj.update(session_key=request.session.session_key) # 更新新登录user的session_key
return redirect('/index/')
else:
return redirect('/accounts/login/')