Zookeeper分布式锁(多进程竞争)实现的代码示例分享

zookeeper分布式锁在实际的场景中应用很多,比如集群中多个节点的leader选举,数据库master-slave模式的主库的选择等等
解决方案依然很简单,需要加锁的进程先尝试在zookeeper上创建一个临时节点L,如果创建成功则加锁成功,
如果不成功(已存在)则在该节点上设置watch。
进程通过删除L来解锁(当进程意外终止,L也会被删除,不会造成死锁),当L被删除时,其它等待锁的进程会得到通知,
此时这些进程再次创建L来获得锁。
上面的方案,当竞争锁的进程比较多时,解锁时会引起Herd Effect,可对加锁规则进行限制,如按进程尝试加锁的顺序来分配锁。
在zookeeper上,每个加锁的进程创建一个带SEQUENTIAL标志的临时节点,每次让序号最小的节点获得锁,
这样每个节点只需要watch它前面节点的状态即可,当其前面
节点被删除时,其将被通知,并获得锁。
以下是笔者在项目中的分布式锁的示例代码,可供参考

zookeeper中的节点
    /election
          |---90338461809508352-192.168.1.111:8983
          |---90338461809508351-192.168.1.112:8983
          |---90338461809508356-192.168.1.113:8983
    /leader(90338461809508351-192.168.1.112:8983)

代码示例:

/**
 * 分布式锁应用示例
 * 多个进程节点的leader选举
 * @author yangbutao
 * 
 */
public class ElectionMain {
	private static Logger log = LoggerFactory.getLogger(ElectionMain.class);

	private final static Pattern LEADER_SEQ = Pattern
			.compile(".*?/?.*?-n_(\\d+)");
	private final static Pattern SESSION_ID = Pattern
			.compile(".*?/?(.*?-.*?)-n_\\d+");

	public static void main(String[] args)throws Exception {
		ZooKeeper zkClient = new ZooKeeper("10.1.1.20:2222", 3000, null);
		String electionPath = "/election";
		//当前节点名称
		String coreNodeName = "192.168.1.111:8983";
		//客户端节点的sessionId
		long sessionId = zkClient.getSessionId();
		String id = sessionId + "-" + coreNodeName;
		String leaderSeqPath = null;
		boolean cont = true;
		int tries = 0;
		while (cont) {
			try {
				leaderSeqPath = zkClient.create(
						electionPath + "/" + id + "-n_", null,
						ZooDefs.Ids.OPEN_ACL_UNSAFE,
						CreateMode.EPHEMERAL_SEQUENTIAL);
				cont = false;
			} catch (ConnectionLossException e) {
				List<String> entries = zkClient.getChildren(electionPath, true);
				boolean foundId = false;
				for (String entry : entries) {
					String nodeId = getNodeId(entry);
					if (id.equals(nodeId)) {
						foundId = true;
						break;
					}
				}
				if (!foundId) {
					cont = true;
					if (tries++ > 20) {
						throw new Exception( "server error", e);
					}
					try {
						Thread.sleep(50);
					} catch (InterruptedException e2) {
						Thread.currentThread().interrupt();
					}
				}

			} catch (KeeperException.NoNodeException e) {
				if (tries++ > 20) {
					throw new Exception( "server error", e);
				}
				cont = true;
				try {
					Thread.sleep(50);
				} catch (InterruptedException e2) {
					Thread.currentThread().interrupt();
				}
			}
		}
		int seq = getSeq(leaderSeqPath);
		checkIfIamLeader(zkClient, seq);
	}

	private static String getNodeId(String nStringSequence) {
		String id;
		Matcher m = SESSION_ID.matcher(nStringSequence);
		if (m.matches()) {
			id = m.group(1);
		} else {
			throw new IllegalStateException("Could not find regex match in:"
					+ nStringSequence);
		}
		return id;
	}

	private static int getSeq(String nStringSequence) {
		int seq = 0;
		Matcher m = LEADER_SEQ.matcher(nStringSequence);
		if (m.matches()) {
			seq = Integer.parseInt(m.group(1));
		} else {
			throw new IllegalStateException("Could not find regex match in:"
					+ nStringSequence);
		}
		return seq;
	}

	/**
	 * 排序seq
	 */
	private static void sortSeqs(List<String> seqs) {
		Collections.sort(seqs, new Comparator<String>() {

			@Override
			public int compare(String o1, String o2) {
				return Integer.valueOf(getSeq(o1)).compareTo(
						Integer.valueOf(getSeq(o2)));
			}
		});
	}

	private static List<Integer> getSeqs(List<String> seqs) {
		List<Integer> intSeqs = new ArrayList<Integer>(seqs.size());
		for (String seq : seqs) {
			intSeqs.add(getSeq(seq));
		}
		return intSeqs;
	}

	private static void checkIfIamLeader(final ZooKeeper zkClient, final int seq)
			throws KeeperException, InterruptedException, IOException {
		// get all other numbers...
		final String holdElectionPath = "/election";
		List<String> seqs = zkClient.getChildren(holdElectionPath, true);
		sortSeqs(seqs);
		List<Integer> intSeqs = getSeqs(seqs);
		if (intSeqs.size() == 0) {
			return;
		}
		if (seq <= intSeqs.get(0)) {
			//删除来的leader节点
			try {
				zkClient.delete("/leader", -1);
			} catch (Exception e) {
				// fine
			}
			String seqStr = null;
			for (String currSeq : seqs) {
				if (getSeq(currSeq) == seq) {
					seqStr = currSeq;
					break;
				}
			}
			runIamLeaderProcess(zkClient, seqStr);
		} else {
			// I am not the leader - watch the node below me
			//当前节点不是leader,watcher比我小的节点
			int i = 1;
			for (; i < intSeqs.size(); i++) {
				int s = intSeqs.get(i);
				if (seq < s) {
					// we found who we come before - watch the guy in front
					//发现比我小的节点(节点列表全面经过排序),退出循环
					break;
				}
			}
			int index = i - 2;
			if (index < 0) {
				log.warn("Our node is no longer in line to be leader");
				return;
			}
			try {
				//监控比当前节点seq次小的节点的值变化
				zkClient.getData(holdElectionPath + "/" + seqs.get(index),
						new Watcher() {

							@Override
							public void process(WatchedEvent event) {
								if (EventType.None.equals(event.getType())) {
									return;
								}
								// 检查是否是可以做为leader
								try {
									checkIfIamLeader(zkClient, seq);
								} catch (InterruptedException e) {
									Thread.currentThread().interrupt();
									log.warn("", e);
								} catch (IOException e) {
									log.warn("", e);
								} catch (Exception e) {
									log.warn("", e);
								}
							}

						}, null, true);
			} catch (Exception e) {
				log.warn("Failed setting watch", e);
				checkIfIamLeader(zkClient, seq);
			}
		}
	}

	
	protected static void runIamLeaderProcess(ZooKeeper zkClient, String seqStr)
			throws KeeperException, InterruptedException, IOException {
		final String id = seqStr.substring(seqStr.lastIndexOf("/") + 1);
		//设置leader节点
		zkClient.create("/leader", id.getBytes(),
				ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
	}

}


 

 

### 回答1: 使用 ZooKeeper 实现分布式锁最常见的方法是使用公平锁(Fair Lock)。在这种情况下,在 ZooKeeper 集群中创建一个特殊的临时节点,并使用它来保持锁定状态。当一个客户端尝试获取锁时,它会尝试创建这个临时节点,如果节点创建成功,则说明该客户端已经获得了锁。 使用 ZooKeeper 进行分布式锁实现需要使用 ZooKeeper 的原子操作,例如创建节点和监视节点,以保证锁的正确性。 代码示例如下: ```java public class DistributedLock { private ZooKeeper zk; private String lockNode; private String lockPath; public DistributedLock(ZooKeeper zk, String lockNode) { this.zk = zk; this.lockNode = lockNode; this.lockPath = "/locks/" + lockNode; } public void lock() throws Exception { while (true) { try { zk.create(lockPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); return; } catch (KeeperException.NodeExistsException e) { waitForLock(); } } } public void unlock() throws Exception { zk.delete(lockPath, -1); } private void waitForLock() throws Exception { CountDownLatch latch = new CountDownLatch(1); Watcher lockWatcher = new Watcher() { @Override public void process(WatchedEvent event) { if (event.getType() == EventType.NodeDeleted) { latch.countDown(); } } }; zk.exists(lockPath, lockWatcher); latch.await(); } } ``` 该代码示例提供了一个简单的实现,可以作为创建自己的分布式锁的基 ### 回答2: 在基于 ZooKeeper 实现 Java 分布式锁的过程中,可以按照以下步骤进行: 1. 创建一个基于 ZooKeeper 的客户端对象。 2. 在 ZooKeeper 上创建一个持久化的节点,作为锁的根节点。 3. 当需要进行锁操作时,创建一个临时有序节点作为当前请求的锁节点。 4. 调用 `getChildren` 方法获取锁根节点下的所有节点,并根据节点的序号进行排序。 5. 检查当前节点是否是锁根节点下序号最小的节点,如果是则获取到锁,执行业务逻辑。 6. 如果当前节点不是锁根节点下序号最小的节点,则注册监听锁根节点下序号比自己小一的节点。 7. 当监听到序号比自己小一的节点删除时,重复步骤 4-6 直到获取到锁。 8. 业务逻辑执行完成后,可以删除当前节点,释放锁资源。 此外,还需要特别注意以下几个问题: 1. 分布式锁的超时机制:在创建临时节点时,可以指定一个超时时间,当超过该时间后,如果还未获取到锁,可以删除当前节点,避免死锁。 2. 锁的释放:在业务逻辑执行完成后,需要手动删除当前节点。如果由于某些原因未能正常删除,则需要提供一种机制,在锁节点创建时设置一个 TTL(time-to-live),让 ZooKeeper 在锁节点过期后自动删除。 3. 锁节点的竞争:在并发较高的情况下,可能会出现多个客户端同时创建临时节点的情况。这时可以使用 `CyclicBarrier` 或者 `CountDownLatch` 进行同步,确保每次只有一个客户端创建锁节点。 4. 异常情况的处理:在进行锁操作时,需要处理各种异常情况,比如连接断开、网络超时等,保证系统的稳定性和可靠性。 综上所述,基于 ZooKeeper 可以实现 Java 分布式锁,通过创建临时有序节点和监听上一个节点的删除来实现锁的竞争和获取。 ### 回答3: 实现基于 ZooKeeper 的 Java 分布式锁可以遵循以下步骤: 1. 连接 ZooKeeper:首先,通过 Java API 连接到 ZooKeeper 服务器,可以使用 zookeeper API 提供的 ZooKeeper 类来创建一个连接对象。 2. 创建锁节点:在 ZooKeeper 上创建一个父节点作为锁的根节点,该节点的所有子节点都作为锁节点。可以使用 zookeeper API 的 create() 方法创建临时顺序节点。 3. 获取锁:每个需要获取锁的进程都要通过创建一个临时顺序节点来竞争锁。通过 zookeeper API 的 getChildren() 方法获取锁根节点的所有子节点,如果创建的节点序号是当前所有节点中最小的,则表示获取到了锁。 4. 监听锁节点变化:如果未能获取到锁,应该在创建节点后,使用 zookeeper API 的 exists() 方法注册一个监听器来监听创建的子节点。当监听到创建的子节点发生变化时,判断自己的节点是否变成了最小的节点,如果是则表示获取到了锁。 5. 释放锁:对于已经获取到锁的进程,执行完任务后,需要通过 zookeeper API 的 delete() 方法将自己创建的锁节点删除,这样其他进程就能获取到该锁了。 需要注意的是,在分布式环境下,网络通信可能会出现延迟或故障,因此需要考虑到这些情况来保证分布式锁的正确性和可靠性。此外,还需考虑到异常情况处理、死锁检测和容错等问题,以确保分布式锁的高可用性和可靠性。 以上是使用 ZooKeeper 实现 Java 分布式锁的基本步骤,通过合理地使用 ZooKeeper 的 API,可以轻松实现分布式环境下的锁机制。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值