zoukankan      html  css  js  c++  java
  • ZK注册中心-思路

    一、思路
    1.  服务方启动则将自己注册到zk上,临时节点,节点数据为IP和端口信息
    2. 客户端添加监听器,监听节点变化,每次变化更新本地服务列表
    3. 服务端有问题,则自动摘除节点,依靠临时节点实现
     
    二、注册方实现
       1)添加节点监听器
      
    public class ServiceRegister {
        private  static final String BASE_SERVICES = "/services";
        private static final String  SERVICE_NAME="/products";
        public  static void register(String address,int port) {
            try {
                //产品服务最终会注册倒的地址
                String path = BASE_SERVICES + SERVICE_NAME;
                ZooKeeper zooKeeper =  new ZooKeeper("192.168.112.131:2181",5000, (watchedEvent)->{});
    
    
                createNodeSafe(   zooKeeper,BASE_SERVICES);
                createNodeSafe( zooKeeper ,path );
                //拼接ip和端口
                String server_path = address+":" +port;
                //注册的类型,EPHEMERAL_SEQUENTIAL 零时,并且带序号
                zooKeeper.create(path+"/child",server_path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
                System.out.println("产品服务注册成功");
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        private static void createNodeSafe(ZooKeeper zooKeeper , String path) throws KeeperException, InterruptedException {
            Stat exists = zooKeeper.exists(BASE_SERVICES + SERVICE_NAME, false);
            //先判断服务根路径是否存在
            if(exists == null) {
                zooKeeper.create(path,"".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        }
    }

     2)创建一个监听器

      

    public class InitListener implements ServletContextListener {
        @Override
        //容器初始化的时候会调用,重启tomcat
        public void contextInitialized(ServletContextEvent sce) {
                try {
                    Properties properties =  new Properties();
                    properties.load(InitListener.class.getClassLoader().getResourceAsStream("application.properties"));
                    //获得IP
                    String hostAddress = InetAddress.getLocalHost().getHostAddress();
                    //获得端口
                    int port = Integer.valueOf(properties.getProperty("server.port"));
                    ServiceRegister.register(hostAddress,port);
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
    
    
        }
    }

    3)注册监听器,容器启动的时候加载

    @Bean
    //启动监听起,当tomcat启动的时候,会调用InitListener
    public ServletListenerRegistrationBean servletListenerRegistrationBean() {
        ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
        servletListenerRegistrationBean.setListener(new InitListener());
        return  servletListenerRegistrationBean;
    }
    三、使用方
    1)服务启动添加节点监听器
     
      
    public class InitListener implements ServletContextListener {
        private  static final String BASE_SERVICES = "/services";
        private static final String  SERVICE_NAME="/products";
        private  static AtomicInteger errorTryTimes = new AtomicInteger(0);
        private static final String zkAddr ="192.168.112.131:2181" ;
        private static final Integer zkTimeout = 5000 ;
        private   ZooKeeper zooKeeper;
        private   void init()  {
            try {
                //连接上zk 获得列表信息
                zooKeeper = new ZooKeeper(zkAddr ,zkTimeout,(watchedEvent)->{
                      //当有节点变更的时候通知倒orderService
                    if(watchedEvent.getType() == Watcher.Event.EventType.NodeChildrenChanged
                            && watchedEvent.getPath().equals(BASE_SERVICES+SERVICE_NAME)) {
                        updateServerList();
                    }
                });
    
                //第一次连接的时候要获得列表
                updateServerList();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private  void updateServerList() {
            List<String> newServerList = new ArrayList<>();
            try {
                List<String> children = zooKeeper.getChildren(BASE_SERVICES  + SERVICE_NAME, true);
                for(String subNode:children) {
                    byte[] data = zooKeeper.getData(BASE_SERVICES  + SERVICE_NAME + "/" + subNode, false, null);
                    String host = new String(data, "utf-8");
                    System.out.println("host:"+host);
                    newServerList.add(host);
                }
                LoadBalance.SERVICE_LIST = newServerList;
            } catch (Exception e) {
                //可能异常导致本地列表没有更新,则异常可以重试
                if(errorTryTimes.incrementAndGet()<3){
                    init();
                }
                //打印日志
                e.printStackTrace();
            }
        }
    
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            init();
        }
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
        }
    }
    View Code

     注册监听器:

    @Bean
    public ServletListenerRegistrationBean servletListenerRegistrationBean() {
        ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
        servletListenerRegistrationBean.setListener(new InitListener());
        return  servletListenerRegistrationBean;
    }

    2) 客户端保存服务端的服务列表数据

    public abstract class LoadBalance {
        //最好使用set,这样如果有重复的直接去掉
        public volatile static   List<String> SERVICE_LIST;
        public abstract String choseServiceHost();
    }

    3)简单的负载均衡,访问可以通过随机获得的ip端口,然后拼接对应的参数访问:http或者其他的都可以

    public class RamdomLoadBalance extends  LoadBalance {
        @Override
        public String choseServiceHost() {
            String result = "";
            //SERVICE_LIST 产品 服务的列表
            if(!CollectionUtils.isEmpty(SERVICE_LIST)) {
               // 192.168.30.3:8083
                //192.168.30.3:8084  2
                //index 0,1
                //传入一个种子
                int index = new Random().nextInt(SERVICE_LIST.size());
                result = SERVICE_LIST.get(index);
            }
            return result ;
        }
    }
     
     
     
     
     
     
     
  • 相关阅读:
    php获取某年某月的天数
    处理银行卡每隔4位数用空格隔开(正则表达式)
    刚看到一个前端面试题, 左边固定,右边自适应, 就根据自己想的自己写了下试试
    Yii中利用filters来控制访问
    Yii中使用RBAC完全指南
    自动把 替换成<p></p>
    统计汉字
    php执行linux函数
    java 与 R 相互调用
    Deep Learning 深度学习 学习教程网站集锦(转)
  • 原文地址:https://www.cnblogs.com/lean-blog/p/14150559.html
Copyright © 2011-2022 走看看