package enn.cn.util; /** * Created by Administrator on 2017/9/18. */ import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; public class DistributedLock implements Watcher { private static int threadId; private static ZooKeeper zk = null; private static String selfPath; private static String waitPath; private static String LOG_PREFIX_OF_THREAD; private static final int SESSION_TIMEOUT = 10000; private static String GROUP_PATH; private static String SUB_PATH; private static final String CONNECTION_STRING = "host16:2181"; private static Object object; private static String methodName; private static final int THREAD_NUM = 1; //确保连接zk成功; private CountDownLatch connectedSemaphore = new CountDownLatch(1); //确保所有线程运行结束; private static CountDownLatch threadSemaphore = new CountDownLatch(THREAD_NUM); private static Logger log = LoggerFactory.getLogger(DistributedLock.class); public DistributedLock(int id, String group_path, String sub_path) { this.threadId = id; this.GROUP_PATH = group_path; this.SUB_PATH = sub_path; LOG_PREFIX_OF_THREAD = "【第" + threadId + "个线程】"; } /* * 根据某个对象的名称和方法去执行该方法 * */ public static void execute() { Class clazz = object.getClass(); Method m1; try { m1 = clazz.getDeclaredMethod(methodName); m1.invoke(object); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 获取锁 * * @return */ public void getLock() throws KeeperException, InterruptedException { //EPHEMERAL_SEQUENTIAL临时顺序节点:基本特性与临时节点一致,创建节点的过程中,zookeeper会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名 selfPath = zk.create(SUB_PATH, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); log.info(LOG_PREFIX_OF_THREAD + "创建锁路径:" + selfPath); if (checkMinPath()) { getLockSuccess(); } } /** * 创建节点 * * @param path 节点path * @param data 初始数据内容 * @return */ public boolean createPath(String path, String data, boolean needWatch) throws KeeperException, InterruptedException { if (zk.exists(path, needWatch) == null) { log.info(LOG_PREFIX_OF_THREAD + "节点创建成功, Path: " + this.zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) + ", content: " + data); } return true; } /** * 创建ZK连接 * * @param connectString ZK服务器地址列表 * @param sessionTimeout Session超时时间 */ public void createConnection(String connectString, int sessionTimeout) throws IOException, InterruptedException { zk = new ZooKeeper(connectString, sessionTimeout, this); connectedSemaphore.await(); } /** * 获取锁成功 */ public void getLockSuccess() throws KeeperException, InterruptedException { if (zk.exists(this.selfPath, false) == null) { log.error(LOG_PREFIX_OF_THREAD + "本节点已不在..."); return; } log.info(LOG_PREFIX_OF_THREAD + "获取锁成功!"); execute(); log.info(LOG_PREFIX_OF_THREAD + "删除本节点:" + selfPath); zk.delete(this.selfPath, -1); releaseConnection(); threadSemaphore.countDown(); } /** * 关闭ZK连接 */ public void releaseConnection() { if (this.zk != null) { try { this.zk.close(); } catch (InterruptedException e) { } } log.info(LOG_PREFIX_OF_THREAD + "释放连接"); } /** * 检查自己是不是最小的节点 * * @return */ public boolean checkMinPath() throws KeeperException, InterruptedException { List<String> subNodes = zk.getChildren(GROUP_PATH, false); Collections.sort(subNodes); int index = subNodes.indexOf(selfPath.substring(GROUP_PATH.length() + 1)); switch (index) { case -1: { log.error(LOG_PREFIX_OF_THREAD + "本节点已不在" + selfPath); return false; } case 0: { log.info(LOG_PREFIX_OF_THREAD + "子节点中,我在" + selfPath); return true; } default: { this.waitPath = GROUP_PATH + "/" + subNodes.get(index - 1); log.info(LOG_PREFIX_OF_THREAD + "获取子节点中,排在我前面的" + waitPath); try { zk.getData(waitPath, true, new Stat()); return false; } catch (KeeperException e) { if (zk.exists(waitPath, false) == null) { log.info(LOG_PREFIX_OF_THREAD + "子节点中,排在我前面的" + waitPath + "已不在"); return checkMinPath(); } else { throw e; } } } } } @Override public void process(WatchedEvent event) { if (event == null) { return; } Event.KeeperState keeperState = event.getState(); Event.EventType eventType = event.getType(); if (Event.KeeperState.SyncConnected == keeperState) { if (Event.EventType.None == eventType) { log.info(LOG_PREFIX_OF_THREAD + "成功连接上ZK服务器"); connectedSemaphore.countDown(); } else if (event.getType() == Event.EventType.NodeDeleted && event.getPath().equals(waitPath)) { log.info(LOG_PREFIX_OF_THREAD + "收排我前面节点已被删除"); try { if (checkMinPath()) { getLockSuccess(); } } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } else if (Event.KeeperState.Disconnected == keeperState) { log.info(LOG_PREFIX_OF_THREAD + "与ZK服务器断开连接"); } else if (Event.KeeperState.AuthFailed == keeperState) { log.info(LOG_PREFIX_OF_THREAD + "权限检查失败"); } else if (Event.KeeperState.Expired == keeperState) { log.info(LOG_PREFIX_OF_THREAD + "会话失效"); } } public static int getThreadId() { return threadId; } public static ZooKeeper getZk() { return zk; } public static String getSelfPath() { return selfPath; } public static String getWaitPath() { return waitPath; } public static String getLogPrefixOfThread() { return LOG_PREFIX_OF_THREAD; } public static int getSessionTimeout() { return SESSION_TIMEOUT; } public static String getGroupPath() { return GROUP_PATH; } public static String getSubPath() { return SUB_PATH; } public static String getConnectionString() { return CONNECTION_STRING; } public static Object getObject() { return object; } public static String getMethodName() { return methodName; } public static int getThreadNum() { return THREAD_NUM; } public CountDownLatch getConnectedSemaphore() { return connectedSemaphore; } public static CountDownLatch getThreadSemaphore() { return threadSemaphore; } public static Logger getLog() { return log; } public static void setThreadId(int threadId) { DistributedLock.threadId = threadId; } public static void setZk(ZooKeeper zk) { DistributedLock.zk = zk; } public static void setSelfPath(String selfPath) { DistributedLock.selfPath = selfPath; } public static void setWaitPath(String waitPath) { DistributedLock.waitPath = waitPath; } public static void setLogPrefixOfThread(String logPrefixOfThread) { LOG_PREFIX_OF_THREAD = logPrefixOfThread; } public static void setGroupPath(String groupPath) { GROUP_PATH = groupPath; } public static void setSubPath(String subPath) { SUB_PATH = subPath; } public static void setObject(Object object) { DistributedLock.object = object; } public static void setMethodName(String methodName) { DistributedLock.methodName = methodName; } public void setConnectedSemaphore(CountDownLatch connectedSemaphore) { this.connectedSemaphore = connectedSemaphore; } public static void setThreadSemaphore(CountDownLatch threadSemaphore) { DistributedLock.threadSemaphore = threadSemaphore; } }
package enn.cn.singlepoint; import enn.cn.util.CommonContent; import enn.cn.util.DistributedLock; import org.apache.log4j.Logger; /** * Created by Administrator on 2017/9/20. */ public class SingleSupplyOneMain { private static Logger log = Logger.getLogger(SingleSupplyOneMain.class); static DistributedLock dc; public static void main(String[] args) { new Thread() { @Override public void run() { try { dc = new DistributedLock(1,"/singleLocks","/singleLocks/sub"); dc.createConnection(CommonContent.ZOOKEEPER_HOST, dc.getSessionTimeout()); //GROUP_PATH不存在的话,由一个线程创建 //注:如果一个jar包中有多个main方法要调用同一个zookeeper工具类,那么GROUP_PATH路径要不一样,以为一个main方法会创建一个分组 synchronized (dc.getConnectedSemaphore()) { dc.createPath(dc.getGroupPath(), "该节点由线程" + 1 + "创建", true); } dc.setObject(new TimerManagerTask()); dc.setMethodName("execTask"); dc.getLock(); } catch (Exception e) { log.error("发布时出现异常:{}", e); e.printStackTrace(); } } }.start(); try { dc.getThreadSemaphore().await(); log.info("所有线程运行结束!"); } catch (InterruptedException e) { e.printStackTrace(); } } }