基于ZooKeeper 的锁与基于 Redis 的锁不同之处在于Lock 成功之前会一直阻塞,这与单机场景中的mutex.Lock很相似。

package main

import (
    "github.com/go-zookeeper/zk"
)

func main() {
    c, _, err := zk.Connect([]string{"127.0.0.1"}, time.Second)
    if err != nil {
        panic(err)
    }
    l := zk.NewLock(c, "/lock", zk.WorldACL(zk.PermAll))
    err = l.Lock()
    if err != nil {
        panic(err)
    }
    println("lock success, do your business logic")

    time.Sleep(time.Second * 10) // 模拟业务处理

    l.Unlock()
    println("unlock success, finish business logic")
}

其原理也是基于临时Sequence节点和watch API,例如我们这里使用的是/lock节点。Lock会在该节点下的节点列表中插入自己的值,只要节点下的子节点发生变化,就会通知所有 watch 该节点的程序。这时候程序会检查当前节点下最小的子节点的 id 是否与自己的一致。如果一致,说明加锁成功了。

这种分布式的阻塞锁比较适合分布式任务调度场景,但不适合高频次持锁时间短的抢锁场景。按照 Google 的 Chubby 论文里的阐述,基于强一致协议的锁适用于粗粒度的加锁操作。这里的粗粒度指锁占用时间较长。我们在使用时也应思考在自己的业务场景中使用是否合适。

最后编辑: kuteng  文档更新时间: 2022-03-22 19:29   作者:kuteng