package rpc.impl; import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; 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.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import rpc.NotifyListener; import rpc.Registry; import rpc.URL; import rpc.model.ConsumerConstants; import rpc.model.ProviderConstants; /** * ZK节点格式: * ZK * | * appKey1 appKey2... * | * service1 service2... * | * consumers providers * | | * ip1 ip2... ip1 ip2... * * @author bird * */ public class ZookeeperRegistry implements Registry{ /** * zk连接超时时间 */ private static final int ZK_CONNECT_TIMEOUT = 1000; /** * zookeeper客户端 */ private ZooKeeper zk; /** * zk注册中心全局锁 */ private final Lock mutex = new ReentrantLock(); /** * 连接状态锁 */ private final Condition connCondition = mutex.newCondition(); /** * 注册中心url */ private final URL registryUrl; /** * 标识zookeeper连接中心是否可用 */ private volatile boolean isAvailable; public ZookeeperRegistry(URL registryUrl) throws IOException { Preconditions.checkNotNull(registryUrl); this.registryUrl = registryUrl; // 初始化zk注册中心 init(); } /** * 初始化注册中心 * @throws IOException zookeeper连接异常 */ private void init() throws IOException { // 获取zookeeper连接IP String connectString = registryUrl.connectString(); Preconditions.checkNotNull(connectString); mutex.lock(); try { // 新建zookeeper连接 zk = new ZooKeeper(connectString, ZK_CONNECT_TIMEOUT, new ConnectionWatcher()); // 等待连接完成,超时时间设置为2倍zk连接超时时间 connCondition.await(ZK_CONNECT_TIMEOUT << 1, TimeUnit.MILLISECONDS); // 设置初始状态 this.isAvailable = true; } catch (InterruptedException e) { e.printStackTrace(); }finally { mutex.unlock(); } } private class ConnectionWatcher implements Watcher{ /* * 当连接成功时,供回调 */ @Override public void process(WatchedEvent event) { // 传来NONE类型事件,一般是连接事件等 if(event.getType() == EventType.None) { // 事件状态为:连接中 if(event.getState() == KeeperState.SyncConnected) { // 唤醒等待连接成功的线程 mutex.lock(); try { // 唤醒等待连接成功的线程 connCondition.signalAll(); } finally { mutex.unlock(); } } } } } private class ChildrenWatcher implements Watcher{ /** * 监视器所监视的zk路径 */ private String path; /** * url列表的监听器 */ private NotifyListener listener; public ChildrenWatcher(String path, NotifyListener listener){ this.path = path; this.listener = listener; } @Override public void process(WatchedEvent event) { // 如果和zk服务器保持连接中 if(event.getState() == KeeperState.SyncConnected) { switch(event.getType()) { // providers子节点发生改变,更新服务列表 case NodeChildrenChanged:{ try { // 获取发生变动的列表,重置监听器 List<String> paths = zk.getChildren(path, this); // 通知listener doNotify(listener, paths); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } default :{ //TODO } } } } } @Override public URL getUrl() { return registryUrl; } @Override public boolean isAvailable() { return isAvailable; } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void register(URL url) throws Exception { Preconditions.checkNotNull(url); Preconditions.checkNotNull(url.getPath()); // 获取url对应的zk路径 String providerPath = getProviderPath(url); // 在zk服务器上建立相应路径 zk.create(providerPath, "".getBytes(), null, CreateMode.EPHEMERAL); } @Override public void unregister(URL url) throws Exception { Preconditions.checkNotNull(url); Preconditions.checkNotNull(url.getPath()); // 获取url对应的zk路径 String providerPath = getProviderPath(url); // 在zk服务器上删除相应路径 zk.delete(providerPath, -1); } /** * zookeeper操作: * 1. getChildren /appKey/service/providers/, watcher 获取IP节点列表 * 2. create /appKey/service/consumers/IP 注册信息到IP节点 * * watcher监视IP节点列表的变化 * @param url service url * @param listener * @throws Exception */ @Override public void subscribe(URL url, NotifyListener listener) throws Exception { Preconditions.checkNotNull(url); Preconditions.checkNotNull(url.getPath()); Preconditions.checkNotNull(listener); // 生成provider路径 String providerPath = getProviderPath(url); // 获取IP节点列表 // 新建watcher监听节点目录变化 Watcher watcher = new ChildrenWatcher(providerPath, listener); // 获取IP节点列表 List<String> paths = zk.getChildren(providerPath, watcher); // 通知listener doNotify(listener, paths); // 注册信息到IP节点 // 生成consumer路径 String consumerPath = getConsumerPath(url); // 创建consumer节点 zk.create(consumerPath, "".getBytes(), null, CreateMode.EPHEMERAL); } /** * 实际执行listener回调 * @param listener * @param paths 获取的zk路径信息 */ private void doNotify(NotifyListener listener, List<String> paths) { // 新建不可变url列表的builder ImmutableList.Builder<URL> listBuilder = ImmutableList.builder(); // 将所有获取的path全部转化成url并放入列表 paths.forEach(p -> { URL u = URL.builder().str(p).build(); listBuilder.add(u); }); // 新建url缓存 List<URL> urls = listBuilder.build(); // 回调监听器 listener.notify(urls); } /** * zookeeper操作: * 1. delete /appkey/service/consumer/IP * @param url service url * @param listener * @throws Exception */ @Override public void unsubscribe(URL url, NotifyListener listener) throws Exception { Preconditions.checkNotNull(url); Preconditions.checkNotNull(url.getPath()); Preconditions.checkNotNull(listener); // 生成consumer路径 String consumerPath = getConsumerPath(url); // TODO 版本问题 删除zk上的路径 zk.delete(consumerPath, -1); // 传入空列表,回调监听器 listener.notify(ImmutableList.of()); } /** * zookeeper操作: * 1. getChildren /appKey/service/providers/ * @param url * @return * @throws Exception */ @Override public List<URL> lookup(URL url) throws Exception { Preconditions.checkNotNull(url); Preconditions.checkNotNull(url.getPath()); // 生成provider路径 String providerPath = getProviderPath(url); // 获取IP节点列表 List<String> paths = zk.getChildren(providerPath, false); // 新建不可变url列表的builder ImmutableList.Builder<URL> listBuilder = ImmutableList.builder(); // 将所有获取的path全部转化成url并放入列表 paths.forEach(p -> { URL u = URL.builder().str(p).build(); listBuilder.add(u); }); // 新建url缓存 List<URL> urls = listBuilder.build(); return urls; } /** * 形如: /appKey/service/providers/IP * @param url * @return */ private String getProviderPath(URL url) { Preconditions.checkNotNull(url); // 获取path String path = url.getPath(); // path必须存在且以providers结尾 Preconditions.checkNotNull(path); Preconditions.checkArgument(path.endsWith(ProviderConstants.PROVIDERS));; // 正确格式 /appKey/service/providers/ 至少有三个分隔 StringBuilder builder = new StringBuilder(path); // 组装/appKey/service/providers/IP格式 builder.append(URL.PATH_SPLIT) .append(url.connectString()); return builder.toString(); } /** * 形如: /appKey/service/consumers/IP * @param url * @return */ private String getConsumerPath(URL url) { Preconditions.checkNotNull(url); // 获取path String path = url.getPath(); // path必须存在且以consumes结尾 Preconditions.checkNotNull(path); Preconditions.checkArgument(path.endsWith(ConsumerConstants.CONSUMERS));; // 正确格式 /appKey/service/consumers/ 至少有三个分隔 StringBuilder builder = new StringBuilder(path); // 组装/appKey/service/consumers/IP格式 builder.append(URL.PATH_SPLIT) .append(url.connectString()); return builder.toString(); } }