使用模板模式,对注册中心进行设计,可以方便后续添加注册中心
模板抽象类,提供注册中心必要的方法。
public abstract class ServiceRegistry { //这是一个模板的抽象类,规定了注册中心对外提供的方法 //开始注册服务,参数map中应该包含注册中心要启动所需的参数 public abstract void start(Map<String, String> param); //停止注册中心 public abstract void stop(); //注册,是从服务端将服务注册到注册中心上,keys表示服务端所有的servicekey值,而value表示服务端的地址。 public abstract boolean registry(Set<String> keys, String value); //移除,从注册中心上将服务移除 public abstract boolean remove(Set<String> keys, String value); //发现,客户端从注册中心上发现服务 public abstract Map<String, TreeSet<String>> discovery(Set<String> keys); //发现重载,这里客户端会发现servicekey下的所有地址。 public abstract TreeSet<String> discovery(String key); }
创建一个本地的注册中心,使用集合来存储注册的数据,实现模板的方法
public class LocalServiceRegistry extends ServiceRegistry { //本地注册中心的实现 //定义一个map集合来存放注册的数据 private Map<String, TreeSet<String>> registryData;// @Override public void start(Map<String, String> param) { registryData = new HashMap<String, TreeSet<String>>(); } @Override public void stop() { registryData.clear();//清理数据 } @Override public boolean registry(Set<String> keys, String value) {//这里传来的是地址和servicekey if (keys==null || keys.size()==0 || value==null || value.trim().length()==0) { return false; } for (String key : keys) {//取出每一个服务key值 TreeSet<String> values = registryData.get(key);//尝试去取服务的key值 if (values == null) { values = new TreeSet<>(); registryData.put(key, values);//如果取不到,则将每一个服务key都存成一个key值,而values是新建的tree类 } values.add(value);//每次通过不同的地址传来相同服务key,就将地址存到values中。 //这样保证了注册中心的服务key对应了来自不同地址的服务器。 } return true; } @Override public boolean remove(Set<String> keys, String value) { if (keys==null || keys.size()==0 || value==null || value.trim().length()==0) { return false; } for (String key : keys) { TreeSet<String> values = registryData.get(key); if (values != null) { values.remove(value); } //移除每一个key值下的地址 } return true; } @Override public Map<String, TreeSet<String>> discovery(Set<String> keys) { if (keys==null || keys.size()==0) { return null; } Map<String, TreeSet<String>> registryDataTmp = new HashMap<String, TreeSet<String>>(); for (String key : keys) { TreeSet<String> valueSetTmp = discovery(key); if (valueSetTmp != null) { registryDataTmp.put(key, valueSetTmp); } //将每个key值下的地址都取出来,一般不用,只取一个 } return registryDataTmp; } @Override public TreeSet<String> discovery(String key) { return registryData.get(key); //返回其中的一个key值对应的地址 } }
使用zk作为注册中心。
为什么要选用注册中心
假设没有注册中心,采用直连的方式,如果服务提供者发生变化,那么消费者也要立即更新,耦合度太高
zk作为服务注册的一个框架,消费者只需要向注册中心获取服务提供者的地址,无需自己做更新。达到了解耦合的作用,而且还能实现服务的自动发现。
public class ZkServiceRegistry extends ServiceRegistry { //定义zk的参数 public static final String ENV = "env"; //zk的环境 public static final String ZK_ADDRESS = "zkaddress"; //zk的地址 public static final String ZK_DIGEST = "zkdigest"; //zk的授权方式 //配置 private static final String zkBasePath = "/xxl-rpc"; //zk的基础路径 private String zkEnvPath; //zk的环境地址 private XxlZkClient xxlZkClient = null; //zk的操作端 自己定义 private Thread refreshThread;//定义一个刷新的线程,用来执行当服务端地址改变后,注册中心需要刷新改变 private volatile boolean refreshThreadStop = false;//刷新停止的标志位,线程可见的。 //设置两个map集合,来存注册的数据和发现的数据 private volatile ConcurrentMap<String, TreeSet<String>> registryData = new ConcurrentHashMap<String, TreeSet<String>>(); private volatile ConcurrentMap<String, TreeSet<String>> discoveryData = new ConcurrentHashMap<String, TreeSet<String>>(); //提供从key值到路径的函数 public String keyToPath(String nodeKey){ return zkEnvPath + "/" + nodeKey; } //从路径到key值 public String pathToKey(String nodePath){ if (nodePath==null || nodePath.length() <= zkEnvPath.length() || !nodePath.startsWith(zkEnvPath)) { return null; } return nodePath.substring(zkEnvPath.length()+1, nodePath.length()); } //开启注册 @Override public void start(Map<String, String> param) { //从服务端传来的数据中取到zk所需的参数 String zkaddress = param.get(ZK_ADDRESS); String zkdigest = param.get(ZK_DIGEST); String env = param.get(ENV); //验证地址和环境是否为空 // valid if (zkaddress == null || zkaddress.trim().length() == 0) { throw new XxlRpcException("xxl-rpc zkaddress can not be empty"); } if (env == null || env.trim().length() == 0) { throw new XxlRpcException("xxl-rpc env can not be empty"); } //初始化环境地址 zkEnvPath = zkBasePath.concat("/").concat(env); //配置客户端 //需要完善zk的客户端 xxlZkClient = new XxlZkClient(zkaddress, zkEnvPath, zkdigest, new Watcher() { @Override public void process(WatchedEvent watchedEvent) { try { // session expire, close old and create new if (watchedEvent.getState() == Event.KeeperState.Expired) {//如果观察者失效 //删除旧的,创建新的 xxlZkClient.destroy(); xxlZkClient.getClient(); //刷新发现的数据 refreshDiscoveryData(null); } //得到key值 String path = watchedEvent.getPath(); String key = pathToKey(path); if (key != null) {//如果key不为空 xxlZkClient.getClient().exists(path, true);// // refresh if (watchedEvent.getType() == Event.EventType.NodeChildrenChanged) {//如果子节点发生改变,则执行刷新的方法 // refreshDiscoveryData (one):one change refreshDiscoveryData(key); } else if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { //打印日志,同步连接 } } } catch (Exception e) { //打印日志 } } }); //初始化客户端 xxlZkClient.getClient(); //刷新的线程启动 refreshThread=new Thread(new Runnable() { @Override public void run() { //refreshThreadStop一直使false,直到stop,因此,这个线程一直在运行 while (!refreshThreadStop) { try { TimeUnit.SECONDS.sleep(60);// // refreshDiscoveryData (all):cycle check refreshDiscoveryData(null); //刷新发现的数据 refreshRegistryData(); //刷新注册的数据 } catch (Exception e) { if (!refreshThreadStop) { //打印日志 } } } } }); refreshThread.setName("xxl-rpc, ZkServiceRegistry refresh thread."); refreshThread.setDaemon(true);//设置为守护线程 refreshThread.start(); } //注册停止 @Override public void stop() { if (xxlZkClient!=null) { xxlZkClient.destroy(); } if (refreshThread != null) { refreshThreadStop = true; refreshThread.interrupt(); } } private void refreshDiscoveryData(String key){ //新建一个set集合来存放key值 Set<String> keys = new HashSet<String>(); if (key!=null && key.trim().length()>0) { keys.add(key);//将key值都加到key里面 } else { if (discoveryData.size() > 0) { keys.addAll(discoveryData.keySet()); } } if (keys.size() > 0) { for (String keyItem: keys) { // add-values String path = keyToPath(keyItem); //子路径中的数据就是请求的地址 Map<String, String> childPathData = xxlZkClient.getChildPathData(path); // exist-values TreeSet<String> existValues = discoveryData.get(keyItem); if (existValues == null) { existValues = new TreeSet<String>(); discoveryData.put(keyItem, existValues); } if (childPathData.size() > 0) { existValues.clear(); existValues.addAll(childPathData.keySet()); } } } } //刷新注册数据 private void refreshRegistryData(){ if (registryData.size() > 0) { for (Map.Entry<String, TreeSet<String>> item: registryData.entrySet()) { String key = item.getKey(); for (String value:item.getValue()) { // make path, child path String path = keyToPath(key); xxlZkClient.setChildPathData(path, value, ""); } } } } @Override public boolean registry(Set<String> keys, String value) { //在客户端里设置数据 for (String key : keys) { // local cache TreeSet<String> values = registryData.get(key); if (values == null) { values = new TreeSet<>(); registryData.put(key, values); } values.add(value); // make path, child path String path = keyToPath(key); xxlZkClient.setChildPathData(path, value, ""); } return true; } @Override public boolean remove(Set<String> keys, String value) { for (String key : keys) { TreeSet<String> values = discoveryData.get(key); if (values != null) { values.remove(value); } String path = keyToPath(key); xxlZkClient.deleteChildPath(path, value); } return true; } @Override public Map<String, TreeSet<String>> discovery(Set<String> keys) { if (keys==null || keys.size()==0) { return null; } Map<String, TreeSet<String>> registryDataTmp = new HashMap<String, TreeSet<String>>(); for (String key : keys) { TreeSet<String> valueSetTmp = discovery(key); if (valueSetTmp != null) { registryDataTmp.put(key, valueSetTmp); } } return registryDataTmp; } @Override public TreeSet<String> discovery(String key) { TreeSet<String> values = discoveryData.get(key); if (values == null) { // refreshDiscoveryData (one):first use refreshDiscoveryData(key); values = discoveryData.get(key); } return values; } }