• redis(二十四):Redis分布式锁以及实现(python)


    阅读目录

    1.   什么事分布式锁
    2.   基于redis实现分布式锁

     

    一、什么是分布式锁

    我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的锁进行处理,并且可以完美的运行,毫无Bug!

    注意这是单机应用,后来业务发展,需要做集群,一个应用需要部署到几台机器上然后做负载均衡,大致如下图:

    上图可以看到,变量A存在三个服务器内存中(这个变量A主要体现是在一个类中的一个成员变量,是一个有状态的对象),如果不加任何控制的话,变量A同时都会在分配一块内存,三个请求发过来同时对这个变量操作,显然结果是不对的!即使不是同时发过来,三个请求分别操作三个不同内存区域的数据,变量A之间不存在共享,也不具有可见性,处理的结果也是不对的!

    如果我们业务中确实存在这个场景的话,我们就需要一种方法解决这个问题!

    为了保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用并发处理相关的功能进行互斥控制。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的应用并不能提供分布式锁的能力。为了解决这个问题就需要一种跨机器的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

    分布式锁应该具备哪些条件:

      1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
      2、高可用的获取锁与释放锁;
      3、高性能的获取锁与释放锁;
      4、具备可重入特性;
      5、具备锁失效机制,防止死锁;
      6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

    二、基于redis实现分布式锁

    1、选用Redis实现分布式锁原因:

    (1)Redis有很高的性能;
    (2)Redis命令对此支持较好,实现起来比较方便

    2、使用命令介绍:

    (1)SETNX

      SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。

    (2)expire

      expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

    (3)delete

      delete key:删除key

      在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。

    3、实现思想:

    (1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

    (2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。

    (3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

    复制代码

    复制代码

    #连接redis
    import time
    import uuid
    from threading import Thread
    
    import redis
    
    redis_client = redis.Redis(host="localhost",
                               port=6379,
                               # password=123,
                               db=10)
    
    #获取一个锁
    # lock_name:锁定名称
    # acquire_time: 客户端等待获取锁的时间
    # time_out: 锁的超时时间
    def acquire_lock(lock_name, acquire_time=10, time_out=10):
        """获取一个分布式锁"""
        identifier = str(uuid.uuid4())
        end = time.time() + acquire_time
        lock = "string:lock:" + lock_name
        while time.time() < end:
            if redis_client.setnx(lock, identifier):
                # 给锁设置超时时间, 防止进程崩溃导致其他进程无法获取锁
                redis_client.expire(lock, time_out)
                return identifier
            elif not redis_client.ttl(lock):
                redis_client.expire(lock, time_out)
            time.sleep(0.001)
        return False
    
    #释放一个锁
    def release_lock(lock_name, identifier):
        """通用的锁释放函数"""
        lock = "string:lock:" + lock_name
        pip = redis_client.pipeline(True)
        while True:
            try:
                pip.watch(lock)
                lock_value = redis_client.get(lock)
                if not lock_value:
                    return True
    
                if lock_value.decode() == identifier:
                    pip.multi()
                    pip.delete(lock)
                    pip.execute()
                    return True
                pip.unwatch()
                break
            except redis.excetions.WacthcError:
                pass
        return False
    
    if __name__ == __main__:
        count=10
      def seckill(i):
          identifier=acquire_lock('resource')
          print("线程:{}--获得了锁".format(i))
          time.sleep(1)
          global count
          if count<1:
              print("线程:{}--没抢到,票抢完了".format(i))
              return
          count-=1
          print("线程:{}--抢到一张票,还剩{}张票".format(i,count))
          release_lock('resource',identifier)
    
      # 线程之间通用一个变量, 
      # 此处也可以考虑使用进程, 进程的结果和线程反差很大
      for i in range(50):    t = Thread(target=seckill,args=(i,))   t.start()
  • 相关阅读:
    并发编程学习笔记(15)----Executor框架的使用
    并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理
    并发编程学习笔记(13)----ConcurrentLinkedQueue(非阻塞队列)和BlockingQueue(阻塞队列)原理
    并发编程学习笔记(12)----Fork/Join框架
    并发编程学习笔记(11)----FutureTask的使用及实现
    并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理
    设计模式:代理模式
    设计模式:装饰模式
    设计模式:几大原则
    设计模式:策略模式(Strategy)
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13308885.html
Copyright © 2020-2023  润新知