package com.github.wangxuehui.rpc.snrpc.zookeeper.consumer; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import com.github.wangxuehui.rpc.snrpc.conf.SnRpcConfig; import com.github.wangxuehui.rpc.snrpc.util.Const; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author skyim E-mail:wxh64788665@gmail.com * 类说明 */ public class ServiceConsumer { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceConsumer.class); SnRpcConfig snRpcConfig = SnRpcConfig.getInstance(); // 用于等待 SyncConnected 事件触发后继续执行当前线程 private CountDownLatch latch = new CountDownLatch(1); // 定义一个 volatile 成员变量,用于保存最新的 host,ip地址(考虑到该变量或许会被其它线程所修改,一旦修改后,该变量的值会影响到所有线程) private volatile List<String> urlList = new ArrayList<>(); // 构造器 public ServiceConsumer() { snRpcConfig.loadProperties("snrpcclient.properties"); ZooKeeper zk = connectServer(); // 连接 ZooKeeper 服务器并获取 ZooKeeper 对象 if (zk != null) { watchNode(zk); // 观察 /skyim 节点的所有子节点并更新 urlList 成员变量 } } // 查找 服务 public String lookup() { String service = null; int size = urlList.size(); if (size > 0) { String url; if (size == 1) { url = urlList.get(0); // 若 urlList 中只有一个元素,则直接获取该元素 LOGGER.debug("using only url: {}", url); } else { url = urlList.get(ThreadLocalRandom.current().nextInt(size)); // 若 urlList 中存在多个元素,则随机获取一个元素 LOGGER.debug("using random url: {}", url); } service = url; } return service; } // 连接 ZooKeeper 服务器 private ZooKeeper connectServer() { ZooKeeper zk = null; try { zk = new ZooKeeper(snRpcConfig.getProperty("snrpc.zookeeper.ip"), Const.ZK_SESSION_TIMEOUT, new Watcher() { @Override public void process(WatchedEvent event) { if (event.getState() == Event.KeeperState.SyncConnected) { latch.countDown(); // 唤醒当前正在执行的线程 } } }); latch.await(); // 使当前线程处于等待状态 } catch (IOException | InterruptedException e) { LOGGER.error("", e); } return zk; } // 观察 /skyim 节点下所有子节点是否有变化 private void watchNode(final ZooKeeper zk) { try { List<String> nodeList = zk.getChildren(Const.ZK_REGISTRY_PATH, new Watcher() { @Override public void process(WatchedEvent event) { if (event.getType() == Event.EventType.NodeChildrenChanged) { watchNode(zk); // 若子节点有变化,则重新调用该方法(为了获取最新子节点中的数据) } } }); List<String> dataList = new ArrayList<>(); // 用于存放 /skyim 所有子节点中的数据 for (String node : nodeList) { byte[] data = zk.getData(Const.ZK_REGISTRY_PATH + "/" + node, false, null); // 获取 /skyim 的子节点中的数据 dataList.add(new String(data)); } LOGGER.debug("node data: {}", dataList); urlList = dataList; // 更新最新的 skyim 地址 } catch (KeeperException | InterruptedException e) { LOGGER.error("", e); } } }