最近正在学习ZooKeeper,这个工具有两个比较典型的入门小应用就是配置文件管理和锁实现。
自己写了锁实现分布式程序的不同线程同步处理。
想想有很多漏洞,如断网,服务器不可用等情况,不能完全实现锁安全和准确,只能做到相对安全。
需要在zookeeper上先建立一个/lock目录,存放锁的相关信息。
锁的工具类:
package cn.free8day.zk.lock; import java.util.Collections; import java.util.List; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; /** * 锁工具 * * @author liubq */ public class ZkLock { /** * 构造 * * @param zk */ public ZkLock(ZooKeeper zk) { this.zk = zk; } private ZooKeeper zk; //锁目录 private String LOCK_PATH = "/lock"; private String waitPath = null; private String thisPath = null; final Lock lock = new ReentrantLock(); final Condition zkWait = lock.newCondition(); /** * 加锁 * @throws Exception */ public void lock() throws Exception { thisPath = zk.create(LOCK_PATH + "/sub", Thread.currentThread().getName().getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); lockByzk(false); } /** * 解锁 * * @throws Exception */ public void unLock() throws Exception { unLockByzk(); } /** * 解锁 * * @throws Exception */ private void unLockByzk() throws Exception { zk.delete(this.thisPath, -1); } /** * 加锁 * * @param path * @throws Exception */ private void lockByzk(boolean isEventMode) throws Exception { List<String> children = zk.getChildren(LOCK_PATH, null); Collections.sort(children); String thisNode = thisPath.substring(LOCK_PATH.length() + 1); int index = children.indexOf(thisNode); // 确实是最小节点 if (index == 0) { if (isEventMode) { lock.lock(); try { zkWait.signalAll(); } finally { lock.unlock(); } } return; } // 获得排名比thisPath前1位的节点 this.waitPath = LOCK_PATH + "/" + children.get(index - 1); // 在waitPath上注册监听器, 当waitPath被删除时, zookeeper会回调监听器的process方法 if (zk.exists(waitPath, new Watcher() { @Override public void process(WatchedEvent event) { try { // 发生了waitPath的删除事件 if (event.getType() != EventType.NodeDeleted) { return; } if (!event.getPath().equals(waitPath)) { return; } lockByzk(true); } catch (Exception e) { zkLogger.error(e); } } }) == null) { //没有成功加入监听,则循环重新处理 lockByzk(isEventMode); } else { if (!isEventMode) { lock.lock(); try { children = zk.getChildren(LOCK_PATH, null); Collections.sort(children); thisNode = thisPath.substring(LOCK_PATH.length() + 1); index = children.indexOf(thisNode); // 确实是最小节点 if (index == 0) { return; } else { zkWait.await(); } } finally { lock.unlock(); } } } } } |
测试工具类,只是检查测试。
package cn.free8day.zk.lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooKeeper; public class TMainTest { private static final int len = 190; private final static CountDownLatch latchMain = new CountDownLatch(len); private static List<Integer> count = new ArrayList<Integer>(); public static void main(String[] args) throws Exception { TMainTest test = new TMainTest(); test.connectZookeeper(); int allTi = 1500; test.zk.setData("/ti", String.valueOf(allTi).getBytes(), -1); for (int i = 0; i < len; i++) { Thread t = new Thread("THREAD" + i) { public void run() { try { TMainTest dl = new TMainTest(); for (int i = 0; i < 10; i++) { Thread.sleep(10); dl.consume(); } latchMain.countDown(); } catch (Exception e) { e.printStackTrace(); } } }; t.start(); } latchMain.await(); for (int i = 0; i < allTi; i++) { if (count.get(allTi - 1 - i) != i) { System.out.println("并发错误!"); return; } } System.out.println("并发成功!"); } private void consume() throws Exception { ZkLock lockObj = new ZkLock(connectZookeeper()); lockObj.lock(); byte[] tis = zk.getData("/ti", null, null); String ti = new String(tis); int ticketCount = Integer.valueOf(ti).intValue(); if (ticketCount > 0) { ticketCount = ticketCount - 1; zk.setData("/ti", String.valueOf(ticketCount).getBytes(), -1); System.out.println(Thread.currentThread().getName() + " 消耗了一张票" + " 现在:" + ticketCount); // System.out.println(ticketCount); count.add(ticketCount); } lockObj.unLock(); } private CountDownLatch latch = new CountDownLatch(1); private ZooKeeper zk = null; public ZooKeeper connectZookeeper() throws Exception { if (zk != null) { return zk; } zk = new ZooKeeper("192.168.91.198:2181", 5000, new Watcher() { public void process(WatchedEvent event) { try { // 连接建立时, 打开latch, 唤醒wait在该latch上的线程 if (event.getState() == KeeperState.SyncConnected) { latch.countDown(); return; } } catch (Exception e) { e.printStackTrace(); } } }); // 等待连接建立 latch.await(); return zk; } } |