数据结构:
核心:以节点的形式存储数据,根节点 >> 临时节点(临时序号节点)
zookeeper作为dubbo的注册中心的时候,相关的服务的节点结构
必须注意一点:临时节点下不能存在其他的节点;
服务的节点是临时节点
/dubbo/com.tuling.zk.dubbo.UserService/consumers/consumer://192.168.174.1/com.tuling.zk.dubbo.UserService?application=young-app&category=consumers&check=false&dubbo=2.6.2&interface=com.tuling.zk.dubbo.UserService&methods=getUser&pid=1632&side=consumer&timeout=5000×tamp=1581001093646
/dubbo/com.tuling.zk.dubbo.UserService/providers/dubbo://192.168.174.1:20882/com.tuling.zk.dubbo.UserService?anyhost=true&application=simple-app&dubbo=2.6.2&generic=false&interface=com.tuling.zk.dubbo.UserService&methods=getUser&pid=14592&side=provider&threads=200×tamp=1581001070335
这个属性用来判断临时节点或者永久节点
分布式job:
三个实例节点同时运行,现在需要在某个时间段生成一个报表,那么怎么选择其中的一个节点进行定时任务和报表的生成呢?
方案:选择master作为节点,进行定时任务,并且master节点的序号是最小的
package com.zookeeper.master; import org.I0Itec.zkclient.ZkClient; import java.util.Map; import java.util.stream.Collectors; /** * @author Tommy * Created by Tommy on 2019/9/23 **/ public class MasterResolve { private String server = "192.168.0.88:2181"; private ZkClient zkClient; private static final String rootPath = "/service-master"; private static final String servicePath = rootPath + "/service"; private String nodePath; private volatile boolean master = false; private static MasterResolve resolve; private MasterResolve() { zkClient = new ZkClient(server, 2000, 15000); buildRoot(); createServerNode(); } public static MasterResolve getInstance() { if (resolve == null) { resolve= new MasterResolve(); } return resolve; } // 构建根节点 public void buildRoot() { if (!zkClient.exists(rootPath)) { zkClient.createPersistent(rootPath); } } // 创建server节点 public void createServerNode() { nodePath = zkClient.createEphemeralSequential(servicePath, "slave"); System.out.println("创建service节点:" + nodePath); initMaster(); initListener(); } private void initMaster() { boolean existMaster = zkClient.getChildren(rootPath) .stream() .map(p -> rootPath + "/" + p) .map(p -> zkClient.readData(p)) .anyMatch(d -> "master".equals(d)); if (!existMaster) { doElection(); System.out.println("当前当选master"); } } private void initListener() { zkClient.subscribeChildChanges(rootPath, (parentPath, currentChilds) -> { doElection();// 执行选举 }); } // 执行选举 public void doElection() { Map<String, Object> childData = zkClient.getChildren(rootPath) .stream() .map(p -> rootPath + "/" + p) .collect(Collectors.toMap(p -> p, p -> zkClient.readData(p))); if (childData.containsValue("master")) { return; } childData.keySet().stream().sorted().findFirst().ifPresent(p -> { if (p.equals(nodePath)) { // 设置最小值序号为master 节点 zkClient.writeData(nodePath, "master"); master = true; System.out.println("当前当选master" + nodePath); } }); } public static boolean isMaster() { return getInstance().master; } public static void main(String[] args) { MasterResolve instance = MasterResolve.getInstance(); System.out.println("master"+ MasterResolve.isMaster()); try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } } } 分布式锁:
分布式锁的原理是:
1、基于资源ID创建临时序号读锁节点 /lock/888.R0000000002 Read 2、获取 /lock 下所有子节点,判断其最小的节点是否为读锁,如果是则获锁成功 3、最小节点不是读锁,则阻塞等待。添加lock/ 子节点变更监听。 4、当节点变更监听触发,执行第2步
package com.zookeeper.lock; public class Lock { private String lockId; private String path; private boolean active; public Lock(String lockId, String path) { this.lockId = lockId; this.path = path; } public Lock() { } public String getLockId() { return lockId; } public void setLockId(String lockId) { this.lockId = lockId; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } }
package com.tuling.zookeeper.lock; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import java.util.List; import java.util.stream.Collectors; /** * @author Tommy * Created by Tommy on 2019/9/23 **/ public class ZookeeperLock { private String server = "192.168.0.149:2181"; private ZkClient zkClient; private static final String rootPath = "/service-lock1"; public ZookeeperLock() { zkClient = new ZkClient(server, 5000, 20000); buildRoot(); } // 构建根节点 public void buildRoot() { if (!zkClient.exists(rootPath)) { zkClient.createPersistent(rootPath); } } // 获取锁 public Lock lock(String lockId, long timeout) { // 创建临时节点 Lock lockNode = createLockNode(lockId); lockNode = tryActiveLock(lockNode);// 尝试激活锁 if (!lockNode.isActive()) { try { synchronized (lockNode) { lockNode.wait(timeout); // 线程锁住 } } catch (InterruptedException e) { throw new RuntimeException(e); } } if (!lockNode.isActive()) { throw new RuntimeException(" lock timeout"); } return lockNode; } // 释放锁 public void unlock(Lock lock) { if (lock.isActive()) { zkClient.delete(lock.getPath()); } } // 尝试激活锁 private Lock tryActiveLock(Lock lockNode) { // 获取根节点下面所有的子节点 List<String> list = zkClient.getChildren(rootPath) .stream() .sorted() .map(p -> rootPath + "/" + p) .collect(Collectors.toList()); // 判断当前是否为最小节点 String firstNodePath = list.get(0); // 最小节点是不是当前节点 if (firstNodePath.equals(lockNode.getPath())) { lockNode.setActive(true); } else { String upNodePath = list.get(list.indexOf(lockNode.getPath()) - 1); zkClient.subscribeDataChanges(upNodePath, new IZkDataListener() { @Override public void handleDataChange(String dataPath, Object data) throws Exception { } @Override public void handleDataDeleted(String dataPath) throws Exception { // 事件处理 与心跳 在同一个线程,如果Debug时占用太多时间,将导致本节点被删除,从而影响锁逻辑。 System.out.println("节点删除:" + dataPath); Lock lock = tryActiveLock(lockNode); synchronized (lockNode) { if (lock.isActive()) { lockNode.notify(); // 释放了 } } zkClient.unsubscribeDataChanges(upNodePath, this); } }); } return lockNode; } public Lock createLockNode(String lockId) { String nodePath = zkClient.createEphemeralSequential(rootPath + "/" + lockId, "w"); return new Lock(lockId, nodePath); } }