django 分布式锁实现|8月更文挑战

2,011 阅读1分钟

这是我参与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',
        # 缓存超时时间(默认300None表示永不过期,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/')