惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
Microsoft Security Blog
Microsoft Security Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Vercel News
Vercel News
Application and Cybersecurity Blog
Application and Cybersecurity Blog
C
Check Point Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
W
WeLiveSecurity
The Hacker News
The Hacker News
L
LINUX DO - 热门话题
T
Tenable Blog
Hugging Face - Blog
Hugging Face - Blog
Google Online Security Blog
Google Online Security Blog
博客园 - Franky
P
Proofpoint News Feed
H
Hacker News: Front Page
P
Privacy & Cybersecurity Law Blog
月光博客
月光博客
P
Proofpoint News Feed
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The GitHub Blog
The GitHub Blog
云风的 BLOG
云风的 BLOG
博客园_首页
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
CERT Recently Published Vulnerability Notes
Forbes - Security
Forbes - Security
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
博客园 - 叶小钗
T
Threat Research - Cisco Blogs
aimingoo的专栏
aimingoo的专栏
D
Darknet – Hacking Tools, Hacker News & Cyber Security
小众软件
小众软件
大猫的无限游戏
大猫的无限游戏
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Hacker News - Newest:
Hacker News - Newest: "LLM"
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
O
OpenAI News
G
Google Developers Blog
Martin Fowler
Martin Fowler
罗磊的独立博客
S
SegmentFault 最新的问题
T
Tor Project blog
量子位

博客园 - 干炸小黄鱼

timex 处理时间戳 gorm-gen go 雪花算法 golang每日一库--协程池库ants golang每日一库--json解析库gjson python高级编程-asyncio python高级编程-condition python高级编程-event python装饰器-自动重试 EAP系统 go实现实现 SECS/GEM 协议 设备通信协议 SECS go项目使用Jenkins进行CICD go操作ES mongo db聚合查询 go如何使用mongodb Apache ShardingSphere paxos and raft (分布式一致性算法) go使用 seata 示例 Alibaba 分布式事务 Seata go中使用saga go中使用TCC示例 分布式事务TCC 熔断器 Hystrix OR Sentinel k8s下部署consul and etcd Consul OR Etcd 【力扣hot100】双指针-盛水最多的容器 【力扣hot100】滑动窗口-最小覆盖子串 shell脚本合集 分布式id生成器 springboot通用CURD Python PB级检索系统架构设计 rancher 在三台机器搭建k8s集群 python ssh clinet 数据库排序Null值字段靠后/靠前 常规web项目 docker-compose 例子 手搓一个验证码 使用itertools 中的groupby 对字典数组进行分组后排序 使用开源库 geoip2 获取某ip的经纬度地理信息 python中 apscheduler.schedulers.blocking.BlockingScheduler 定时执行任务 简单的python web项目的docker-compose.yml 示例 python和sliver交互 golang sliver二次开发自定义命令(格式乱后面再调) pydantic做参数校验 基于rancher部署k8s 地理位置相关基础数据 flask migrate时报错 Can't locate revision identified by '3d80e4c025df'
go使用zookeeper分布式锁以及和redis差异
干炸小黄鱼 · 2026-04-21 · via 博客园 - 干炸小黄鱼

ZooKeeper (ZK) 是分布式系统中实现强一致性分布式锁的首选工具之一。与 Redis 追求极致性能不同,ZK 基于 CP 模型(一致性优先),特别适合金融交易、核心配置管理等绝对不能出错的场景。

在 Go 语言中,使用 ZooKeeper 实现分布式锁主要依赖其临时顺序节点Watcher 监听机制

以下是 ZooKeeper 分布式锁的原理介绍及 Go 语言实战代码。


🔒 ZooKeeper 分布式锁的核心原理

ZK 实现锁的机制就像是一个“银行排队取号”系统,它利用三个核心特性来保证锁的可靠性:

  1. 临时节点 (Ephemeral Node) —— 防死锁
    • 客户端创建的节点是临时的。如果客户端宕机或网络断开(Session 失效),ZK 会自动删除该节点。这意味着锁会自动释放,无需像 Redis 那样依赖“看门狗”续期。
  2. 顺序节点 (Sequential Node) —— 公平性
    • 创建节点时,ZK 会自动在节点名后追加一个单调递增的序列号(如 lock-001, lock-002)。这天然形成了一个 FIFO(先进先出)的等待队列。
  3. Watcher 监听机制 —— 高效阻塞(避免羊群效应)
    • 客户端不需要轮询。每个客户端只监听比自己序号小 1 的那个节点
    • 例如,lock-003 只需要监听 lock-002。当 lock-002 被删除(锁释放)时,ZK 通知 lock-003 去尝试获取锁。这避免了所有客户端同时被唤醒争抢锁的“羊群效应”。

💻 Go 语言实现示例

在 Go 中,我们通常使用 go-zookeeper/zk 客户端库。为了让你更直观地理解,我将代码分为底层原理实现(展示逻辑)和生产级封装(推荐用法)。

1. 基础实现(理解原理)

这段代码展示了如何通过创建临时顺序节点和监听前驱节点来实现锁。

package main

import (
	"fmt"
	"sort"
	"time"

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

func main() {
	// 1. 连接 ZooKeeper
	conn, _, err := zk.Connect([]string{"127.0.0.1:2181"}, 5*time.Second)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	lockPath := "/my-distributed-lock"
	
	// 确保根节点存在
	if _, err := conn.Create(lockPath, []byte{}, 0, zk.WorldACL(zk.PermAll)); err != nil && err != zk.ErrNodeExists {
		panic(err)
	}

	// 2. 模拟客户端获取锁
	node, err := acquireLock(conn, lockPath)
	if err != nil {
		fmt.Printf("获取锁失败: %v\n", err)
		return
	}

	fmt.Printf("✅ 锁获取成功!节点: %s\n", node)
	
	// 模拟业务执行时间
	time.Sleep(5 * time.Second)

	// 3. 释放锁(删除节点)
	if err := conn.Delete(node, -1); err != nil {
		fmt.Printf("释放锁失败: %v\n", err)
	} else {
		fmt.Println("🔓 锁已释放")
	}
}

// acquireLock 核心逻辑
func acquireLock(conn *zk.Conn, path string) (string, error) {
	// 创建临时顺序节点
	// zk.FlagEphemeral | zk.FlagSequence 表示创建临时且带序号的节点
	node, err := conn.Create(path+"/lock-", []byte{}, zk.FlagEphemeral|zk.FlagSequence, zk.WorldACL(zk.PermAll))
	if err != nil {
		return "", err
	}

	for {
		// 获取所有子节点
		children, _, err := conn.Children(path)
		if err != nil {
			return "", err
		}

		// 排序节点(因为序号是字符串,需要正确排序)
		sort.Strings(children)

		// 判断自己是否是最小的节点(即队列的第一名)
		if len(children) > 0 && children[0] == node[len(node)-12:] { // 截取节点名后12位进行比对
			return node, nil // 获取锁成功
		}

		// 如果不是第一名,找到比自己小 1 的前驱节点
		var prevNode string
		for i, child := range children {
			if child == node[len(node)-12:] {
				if i > 0 {
					prevNode = path + "/" + children[i-1]
				}
				break
			}
		}

		// 监听前驱节点的删除事件
		if prevNode != "" {
			fmt.Printf("⏳ 等待锁... 监听节点: %s\n", prevNode)
			_, _, ch, err := conn.GetW(prevNode)
			if err != nil {
				return "", err
			}
			// 阻塞等待事件
			event := <-ch
			if event.Err != nil {
				return "", event.Err
			}
			// 收到通知,循环重新检查是否轮到我了
		}
	}
}

2. 生产级用法(使用官方封装)

在实际生产环境中,不建议重复造轮子。go-zookeeper 库内部其实已经封装好了一个 Lock 结构体,使用它更简单、更安全。

package main

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

func main() {
	conn, _, err := zk.Connect([]string{"127.0.0.1:2181"}, 5*time.Second)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// 使用 zk.NewLock 创建锁对象
	// 注意:这里传入的是锁的路径前缀,不需要手动创建父节点
	lock := zk.NewLock(conn, "/my-app/locks", zk.WorldACL(zk.PermAll))

	// 尝试获取锁
	if err := lock.Lock(); err != nil {
		panic(err)
	}
	defer lock.Unlock() // 确保退出时释放

	fmt.Println("✅ 使用封装锁成功!正在执行关键业务...")
	time.Sleep(10 * time.Second)
	fmt.Println("业务完成,锁将自动释放")
}

⚖️ ZooKeeper 锁 vs Redis 锁

在 Go 微服务架构选型时,通常会在 Redis 和 ZK 之间纠结。以下是核心对比:

维度 ZooKeeper 锁 Redis 锁 (Redlock)
一致性模型 CP (强一致性) AP (高可用性,最终一致性)
可靠性 极高。客户端宕机自动释放,无死锁风险。 依赖持久化和主从同步,极端情况可能丢锁。
性能 中等。涉及磁盘 IO 和网络通信,延迟较高 (ms级)。 极高。纯内存操作,微秒级。
实现复杂度 中等。需处理节点监听和会话。 简单。但需处理续期(看门狗)和误删问题。
适用场景 金融交易、元数据管理、低频高可靠任务。 秒杀、高频缓存更新、对延迟极其敏感的场景。

📌 总结与建议

  1. 首选封装:在 Go 中,直接使用 zk.NewLock 即可,它已经帮你处理了顺序节点和 Watcher 的复杂逻辑。
  2. 会话管理:ZK 锁依赖 Session 存活。如果你的 Go 服务网络抖动导致 Session 断开,锁会被立即释放。务必处理好重连逻辑。
  3. 不要滥用:ZK 的写性能有限,不要在超高并发(如每秒数万次)的场景下使用 ZK 锁,否则会把 ZK 集群打挂。那种场景请选用 Redis。