zoukankan      html  css  js  c++  java
  • 基于 Redis 和 ZooKeeper 的分布式锁

    Redis 锁

    SET key value EX 120 NX
      
    该命令是原子操作,表示只有在 key 不存在的情况下,才会赋值成功,并且 120 秒后会自动删除,这样就实现了带超时时间的互斥锁功能,获得锁的程序删除 key 就是释放了锁,如果程序出错退出,达到超时时间后也会保证锁能被释放
      
    这种方法在比较极端的情况下可能失效
      程序 A 获取锁
      master 将数据同步到 slave 之前 master 宕机了
      slave 被选为 master 且没有 A 的锁
      程序 B 获取锁成功,这时候程序 A 还在正常运行,导致两个程序同时获得锁
      

    Redisson

    https://redisson.org/
    Redis Java Client with features of In-Memory Data Grid
    Redisson 是基于 redis 的一个 Java 程序,提供了非常丰富的功能,其中就包括了锁
      
    Redisson 提供了多种锁,具体可参考官网
    https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizers
      
    这里以基本的 Lock 为例子

    RLock lock = redisson.getLock("myLock");
    
    // traditional lock method
    lock.lock();
    
    // or acquire lock and automatically unlock it after 10 seconds
    lock.lock(10, TimeUnit.SECONDS);
    
    // or wait for lock aquisition up to 100 seconds 
    // and automatically unlock it after 10 seconds
    boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
    if (res) {
       try {
         ...
       } finally {
           lock.unlock();
       }
    }
    


    ZooKeeper 锁

    ZooKeeper 的节点叫 ZNode,共有 4 种类型

    1. 持久节点 (PERSISTENT)
    2. 持久节点顺序节点(PERSISTENT_SEQUENTIAL)
    3. 临时节点(EPHEMERAL)
    4. 临时顺序节点(EPHEMERAL_SEQUENTIAL)

    通过创建临时节点获取锁
      只有节点不存在时才能创建成功
      临时节点只有在 client 和 server 保持连接的时候才存在,session 断开后会被 server 删除
      阻塞模式下,可以设置获取锁的超时时间,没能成功获取会抛异常
      似乎没有可以设置成功获取锁后多长时间内会自动释放
      删除节点就是释放锁
      存在程序还在跑但 session 断开的异常情况要处理
      
    通过创建临时顺序节点获取锁
      同一个节点下的临时顺序节点,会按照创建的先后顺序编号,可以实现排队机制
      程序 A B C 先后在同一个节点下创建临时顺序节点
      程序在成功创建后会检查所有临时顺序节点,判断自己是不是编号最小的
      A 程序发现自己是最小的于是 A 拿到锁
      B 程序发现 A 的编号在自己之前,于是等待并通过 watcher 监听 A 创建的节点
      C 程序发现 B 的编号在自己之前,于是等待并通过 watcher 监听 B 创建的节点
      A 程序删除节点释放锁
      B 程序通过 watcher 得知,并再次检查自己是否编号最小,如果是就获取锁
      B 程序删除节点释放锁
      C 程序通过 watcher 得知,并再次检查自己是否编号最小,如果是就获取锁
      
    ZooKeeper 的性能应该会比 Redis 差些

    # coding=utf-8
    from kazoo.client import KazooClient
    from kazoo.client import KazooState
    
    zk = KazooClient(host="localhost:2181")
    zk.start()
    
    
    def my_listener(state):
        if state == KazooState.LOST:
            print("KazooState.LOST")
        elif state == KazooState.SUSPENDED:
            print("KazooState.SUSPENDED")
        else:
            print("KazooState.CONNECTED")
    
    
    zk.add_listener(my_listener)
    
    
    # 通过直接创建节点获取锁
    try:
        zk.create('/test/lock/resource', b'app name', makepath=True, ephemeral=True, sequence=False)
    except:
        print("failed to get the lock")
        exit()
    
    zk.delete('/test/lock/resource')
    
    
    # 通过三方包的功能获取锁
    lock = zk.Lock('/test/lock/resource', b'app name')
    if lock.acquire(blocking=True, timeout=None, ephemeral=True):
        # acquire 函数会使用 sequence=True 在 /test/lock/resource 节点下用包含 uuid 的名字创建子节点,这样节点必然创建成功
        # 如果子节点序号不是最小且 blocking=True 则注册 watcher 然后等待,否则序号最小则返回 True,不是最小或异常则返回 False
        # 如果超过了 timeout 时间还没获取则返回 False
        pass
    else:
        print("failed to get the lock")
        exit()
    
    # release 函数会 delete 节点
    lock.release()
    
    




  • 相关阅读:
    php抽象与接口的区别[转载]
    PHP基础知识(一)
    HTML/CSS方法实现下拉菜单
    SQL语句详细汇总[转]
    (5) 控制器和状态
    (4)模型和数据
    (3)理解代理 proxy
    (2)基于原型的类继承
    (1) basic javascript class
    观察者模式
  • 原文地址:https://www.cnblogs.com/moonlight-lin/p/12617219.html
Copyright © 2011-2022 走看看