• rpc中的注册中心


    使用模板模式,对注册中心进行设计,可以方便后续添加注册中心

    模板抽象类,提供注册中心必要的方法。

    复制代码
    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作为服务注册的一个框架,消费者只需要向注册中心获取服务提供者的地址,无需自己做更新。达到了解耦合的作用,而且还能实现服务的自动发现

     

    XXL-RPC中每个服务在zookeeper中对应一个节点,如图"iface name"节点,该服务的每一个provider机器对应"iface name"节点下的一个子节点,如图中"192.168.0.1:9999"、"192.168.0.2:9999"和"192.168.0.3:9999",子节点类型为zookeeper的EPHMERAL(临时节点)类型,该类型节点有个特点,当机器和zookeeper集群断掉连接后节点将会被移除。consumer底层可以从zookeeper获取到可提供服务的provider集群地址列表,从而可以向其中一个机器发起RPC调用。

    复制代码
    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;
        }
    }
    复制代码

    zk的注册中心类提供了一系列的方法,并通过定义的zkclient类来操作zk,创建和更新节点,这样就可以完成注册的功能

  • 相关阅读:
    Linux下安装jdk
    hadoop下载
    Java:xxx is not an enclosing class
    Android:Gradle报错——No resource found that matches the given name (at 'dialogCornerRadius' with value '?android:attr/dialogCornerRadius')
    TensorFlow:在PyCharm中配置TensorFlow
    Android:屏幕旋转
    Android:onActivityResult详解
    Android:Bundle类
    Android:ConstraintLayout完全解析
    Android:Android Studio生成签名文件,自动签名,以及获取SHA1和MD5值
  • 原文地址:https://www.cnblogs.com/yixiaogo/p/12944383.html
Copyright © 2020-2023  润新知