
Java实现基于ZooKeeper的分布式锁全面解析
一、ZooKeeper分布式锁核心原理
1. 核心机制设计
临时顺序节点 防止客户端崩溃导致死锁 节点监听机制 实现阻塞等待和通知 序号排序 保证锁获取的公平性 可重入计数 支持同一线程多次加锁
2. 锁节点结构示例
bash
/locks
├── lock_000000001
├── lock_000000002
└── lock_000000003
二、基础锁实现方案
1. 同步阻塞锁实现
java
public class ZkDistributedLock {
private final ZooKeeper zk;
private final String lockPath;
private String currentPath;
public void lock() throws Exception {
currentPath = zk.create("/locks/lock_",
new byte,
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
while (true) {
List<String> children = zk.getChildren("/locks", false);
Collections.sort(children);
if (currentPath.equals("/locks/" + children.get(0))) {
return; // 获得锁
}
String prevNode = getPreviousNode(currentPath, children);
final CountDownLatch latch = new CountDownLatch(1);
Stat stat = zk.exists("/locks/" + prevNode, event -> {
if (event.getType() == Event.EventType.NodeDeleted) {
latch.countDown();
}
});
if (stat != null) {
latch.await();
}
}
}
private String getPreviousNode(String current, List<String> children) {
int index = Collections.binarySearch(children,
current.substring("/locks/".length()));
return children.get(index - 1);
}
}
2. 异步回调实现
java
public class AsyncLock {
private final CuratorFramework client;
private InterProcessMutex mutex;
public void asyncLock(String lockPath) {
mutex = new InterProcessMutex(client, lockPath);
mutex.acquireAsync().thenAccept(lockAcquired -> {
if (lockAcquired) {
executeCriticalSection();
}
}).exceptionally(ex -> {
handleLockFailure(ex);
return null;
});
}
private void executeCriticalSection() {
try {
// 执行业务逻辑
} finally {
releaseLock();
}
}
}
三、生产级优化实现
1. 可重入锁实现
java
public class ReentrantZkLock {
private final ThreadLocal<Integer> lockCount = ThreadLocal.withInitial(() -> 0);
private final InterProcessMutex mutex;
public void lock() throws Exception {
if (lockCount.get() == 0) {
mutex.acquire();
lockCount.set(1);
} else {
lockCount.set(lockCount.get() + 1);
}
}
public void unlock() throws Exception {
if (lockCount.get() == 1) {
mutex.release();
lockCount.remove();
} else {
lockCount.set(lockCount.get() - 1);
}
}
}
2. 读写锁实现
java
public class ZkReadWriteLock {
private final InterProcessReadWriteLock rwLock;
public void acquireReadLock() throws Exception {
rwLock.readLock().acquire();
}
public void acquireWriteLock() throws Exception {
rwLock.writeLock().acquire();
}
public void releaseReadLock() throws Exception {
rwLock.readLock().release();
}
public void releaseWriteLock() throws Exception {
rwLock.writeLock().release();
}
}
四、高可用架构设计
1. ZooKeeper集群部署
Leader Election ZAB Protocol Client ZooKeeper Server1 ZooKeeper Server2 ZooKeeper Server3
2. 客户端连接管理
java
public class ZkConnectionManager {
private static final int SESSION_TIMEOUT = 30000;
private static final int CONNECTION_TIMEOUT = 5000;
public CuratorFramework createClient(String clusterAddr) {
return CuratorFrameworkFactory.builder()
.connectString(clusterAddr)
.sessionTimeoutMs(SESSION_TIMEOUT)
.connectionTimeoutMs(CONNECTION_TIMEOUT)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
}
public void handleSessionExpire(CuratorFramework client) {
client.getConnectionStateListenable().addListener((c, newState) -> {
if (newState == ConnectionState.RECONNECTED) {
recoverLocksAfterExpiration();
}
});
}
}
五、异常处理机制
1. 会话过期处理
java
public class SessionExpirationHandler {
private final ConcurrentMap<String, LockState> lockStates = new ConcurrentHashMap<>();
public void registerLock(String path, LockState state) {
lockStates.put(path, state);
}
private void recoverLocksAfterExpiration() {
lockStates.forEach((path, state) -> {
if (state.isLocked()) {
attemptLockRecovery(path);
}
});
}
private void attemptLockRecovery(String path) {
// 实现锁恢复逻辑
}
}
2. 网络分区处理
java
public class NetworkPartitionHandler {
private final CircuitBreaker breaker = new CircuitBreaker()
.withFailureThreshold(5, 1)
.withSuccessThreshold(3)
.withTimeout(10);
public void executeWithResilience(Runnable task) {
breaker.executeRunnable(() -> {
if (checkQuorumAvailable()) {
task.run();
} else {
throw new LockException("Quorum unavailable");
}
});
}
private boolean checkQuorumAvailable() {
// 检查集群是否满足法定人数
}
}
六、性能优化策略
1. 批量节点操作优化
java
public class BatchOperator {
public void createNodesInBatch(List<String> paths) throws Exception {
List<Op> ops = paths.stream()
.map(p -> Op.create(p, new byte,
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT))
.collect(Collectors.toList());
zk.multi(ops);
}
}
2. 客户端缓存优化
java
public class NodeCacheManager {
private final Map<String, NodeCache> caches = new ConcurrentHashMap<>();
public void watchNode(String path) throws Exception {
NodeCache cache = new NodeCache(zkClient, path);
cache.getListenable().addListener(() -> {
ChildData data = cache.getCurrentData();
updateLocalState(data);
});
cache.start(true);
caches.put(path, cache);
}
}
七、生产监控指标
1. 关键监控指标
java
class ZkLockMetrics {
// 锁获取耗时
Timer lockAcquireTime = new Timer();
// 锁竞争指标
Counter lockWaitCount = new Counter();
Histogram lockWaitTime = new Histogram();
// ZK连接状态
Gauge zkSessionState = new Gauge();
// 节点数量统计
Gauge znodeCount = new Gauge();
}
2. ZooKeeper服务端监控
bash
# 四字命令监控
echo mntr | nc zk1 2181
八、与其他方案对比
特性 | ZooKeeper方案 | Redis方案 | etcd方案 |
---|---|---|---|
一致性模型 | 顺序一致性(ZAB协议) | 最终一致性 | 强一致性(Raft协议) |
锁释放机制 | 会话结束自动释放 | 依赖TTL自动过期 | Lease自动过期 |
可重入性 | 原生支持 | 需自行实现 | 需自行实现 |
读写锁支持 | 原生支持 | 需自行实现 | 需自行实现 |
性能 | 1万+ QPS | 10万+ QPS | 2万+ QPS |
部署复杂度 | 较高 | 简单 | 中等 |
九、典型应用场景
1. 主从选举
java
public class LeaderElection {
private final LeaderSelector leaderSelector;
public LeaderElection() {
leaderSelector = new LeaderSelector(client, "/election/leader",
new LeaderSelectorListener() {
@Override
public void takeLeadership(CuratorFramework client) {
executeAsLeader();
}
@Override
public void stateChanged(CuratorFramework client,
ConnectionState newState) {
handleConnectionChange(newState);
}
});
}
public void start() {
leaderSelector.autoRequeue();
leaderSelector.start();
}
}
2. 分布式队列
java
public class DistributedQueue {
private final DistributedQueue<String> queue;
public DistributedQueue() {
queue = QueueBuilder.builder(client,
new QueueSerializer<String>() {
public byte[] serialize(String item) {
return item.getBytes();
}
public String deserialize(byte[] bytes) {
return new String(bytes);
}
},
new QueueConsumer<String>() {
public void consumeMessage(String message) {
processMessage(message);
}
})
.build("/queues/test");
}
}
十、最佳实践总结
- 节点路径规划 :使用清晰的命名空间(如
/app/env/resource
) - 会话管理:合理设置会话超时(推荐30-60秒)
- 异常恢复:实现连接状态监听和自动恢复
- 权限控制 :使用ACL保障安全(如
digest
认证模式) - 监控告警:关注znode数量、Watcher数量、数据大小等指标
- 性能调优 :
- 关闭不必要的Watcher
- 使用批处理操作
- 优化序列化方式
- 版本兼容:保持客户端与服务端版本兼容
通过ZooKeeper实现分布式锁具有强一致性、原生可重入、公平排队等优势,适合金融交易、配置管理等对一致性要求高的场景。需注意ZooKeeper的写性能限制,建议在写操作低于1万QPS的场景中使用。
更多资源:
http://sj.ysok.net/jydoraemon 访问码:JYAM