zoukankan      html  css  js  c++  java
  • springboot使用zookeeper(curator)实现注册发现与负载均衡

    最简单的实现服务高可用的方法就是集群化,也就是分布式部署,但是分布式部署会带来一些问题。比如:

    1、各个实例之间的协同(锁)

    2、负载均衡

    3、热删除

    这里通过一个简单的实例来说明如何解决注册发现和负载均衡。

    1、先解决依赖,这里只给出zk相关的依赖,pom.xml如下

      <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.4.8</version>
            </dependency>
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-recipes</artifactId>
                <version>2.9.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-client</artifactId>
                <version>2.9.1</version>
      </dependency>

    2、ZkClient

    这里使用的是curator,curator是对zookeeper的简单封装,提供了一些集成的方法,或者是提供了更优雅的api,举例来说

    zk的create(path, mode, acl, data)方法 == curator create().withMode(mode).forPath(path)调用链

    package com.dqa.prometheus.client.zookeeper;
    
    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.net.InetAddress;
    import java.util.ArrayList;
    import java.util.List;
    
    public class ZkClient {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        private CuratorFramework client;
        private String zookeeperServer;
        private int sessionTimeoutMs;
        private int connectionTimeoutMs;
        private int baseSleepTimeMs;
        private int maxRetries;
    
        public void setZookeeperServer(String zookeeperServer) {
            this.zookeeperServer = zookeeperServer;
        }
        public String getZookeeperServer() {
            return zookeeperServer;
        }
        public void setSessionTimeoutMs(int sessionTimeoutMs) {
            this.sessionTimeoutMs = sessionTimeoutMs;
        }
        public int getSessionTimeoutMs() {
            return sessionTimeoutMs;
        }
        public void setConnectionTimeoutMs(int connectionTimeoutMs) {
            this.connectionTimeoutMs = connectionTimeoutMs;
        }
        public int getConnectionTimeoutMs() {
            return connectionTimeoutMs;
        }
        public void setBaseSleepTimeMs(int baseSleepTimeMs) {
            this.baseSleepTimeMs = baseSleepTimeMs;
        }
        public int getBaseSleepTimeMs() {
            return baseSleepTimeMs;
        }
        public void setMaxRetries(int maxRetries) {
            this.maxRetries = maxRetries;
        }
        public int getMaxRetries() {
            return maxRetries;
        }
    
        public void init() {
            RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries);
            client = CuratorFrameworkFactory.builder().connectString(zookeeperServer).retryPolicy(retryPolicy)
                    .sessionTimeoutMs(sessionTimeoutMs).connectionTimeoutMs(connectionTimeoutMs).build();
            client.start();
        }
    
        public void stop() {
            client.close();
        }
    
        public CuratorFramework getClient() {
            return client;
        }
    
        public void register() {
            try {
                String rootPath = "/" + "services";
                String hostAddress = InetAddress.getLocalHost().getHostAddress();
                String serviceInstance = "prometheus" + "-" +  hostAddress + "-";
                client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(rootPath + "/" + serviceInstance);
            } catch (Exception e) {
                logger.error("注册出错", e);
            }
        }
    
        public List<String> getChildren(String path) {
            List<String> childrenList = new ArrayList<>();
            try {
                childrenList = client.getChildren().forPath(path);
            } catch (Exception e) {
                logger.error("获取子节点出错", e);
            }
            return childrenList;
        }
    
        public int getChildrenCount(String path) {
            return getChildren(path).size();
        }
    
        public List<String> getInstances() {
            return getChildren("/services");
        }
    
        public int getInstancesCount() {
            return getInstances().size();
        }
    }

    2、configuration如下

    package com.dqa.prometheus.configuration;
    
    
    import com.dqa.prometheus.client.zookeeper.ZkClient;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ZkConfiguration {
        @Value("${zookeeper.server}")
        private String zookeeperServer;
        @Value(("${zookeeper.sessionTimeoutMs}"))
        private int sessionTimeoutMs;
        @Value("${zookeeper.connectionTimeoutMs}")
        private int connectionTimeoutMs;
        @Value("${zookeeper.maxRetries}")
        private int maxRetries;
        @Value("${zookeeper.baseSleepTimeMs}")
        private int baseSleepTimeMs;
    
        @Bean(initMethod = "init", destroyMethod = "stop")
        public ZkClient zkClient() {
            ZkClient zkClient = new ZkClient();
            zkClient.setZookeeperServer(zookeeperServer);
            zkClient.setSessionTimeoutMs(sessionTimeoutMs);
            zkClient.setConnectionTimeoutMs(connectionTimeoutMs);
            zkClient.setMaxRetries(maxRetries);
            zkClient.setBaseSleepTimeMs(baseSleepTimeMs);
            return zkClient;
        }
    
    }

    配置文件如下

    #============== zookeeper ===================
    zookeeper.server=10.93.21.21:2181,10.93.18.34:2181,10.93.18.35:2181
    zookeeper.sessionTimeoutMs=6000
    zookeeper.connectionTimeoutMs=6000
    zookeeper.maxRetries=3
    zookeeper.baseSleepTimeMs=1000

    3、注册发现

    是通过上面封装的ZkClient中的register方法实现的,调用如下。

    package com.dqa.prometheus;
    
    import com.dqa.prometheus.client.zookeeper.ZkClient;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.orm.jpa.EntityScan;
    import org.springframework.context.ApplicationContext;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.annotation.EnableScheduling;
    
    @SpringBootApplication
    @EnableAsync
    @EnableScheduling
    @EntityScan(basePackages="com.xiaoju.dqa.prometheus.model")
    public class Application {
        public static void main(String[] args) {
            ApplicationContext context = SpringApplication.run(Application.class, args);
            ZkClient zkClient = context.getBean(ZkClient.class);
            zkClient.register();
        }
    }

    注册代码说明:

     public void register() {
            try {
                String rootPath = "/" + "services";
                String hostAddress = InetAddress.getLocalHost().getHostAddress();
                String serviceInstance = "prometheus" + "-" +  hostAddress + "-";
                client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(rootPath + "/" + serviceInstance);
            } catch (Exception e) {
                logger.error("注册出错", e);
            }
        }

    1、zk中的注册路径

    /services/prometheus-10.93.21.21-00000000001

    2、CreateMode有四种,选择EPHEMERAL_SEQUENTIAL的原因是,服务关闭的时候session超时,zk节点会自动删除,同时自增id可以实现锁和负载均衡,下面再说

    1、PERSISTENT
    
    持久化目录节点,存储的数据不会丢失。
    
    2、PERSISTENT_SEQUENTIAL
    
    顺序自动编号的持久化目录节点,存储的数据不会丢失,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名。
    
    3、EPHEMERAL
    
    临时目录节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除。 
    
    4、EPHEMERAL_SEQUENTIAL
    
    临时自动编号节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名。

    4、负载均衡

         /*
            *   我是第几个实例, 做负载均衡
            * */
            List<String> instanceList = zkClient.getInstances();
            Collections.sort(instanceList);
            String hostAddress = NetFunction.getAddressHost();
            int instanceNo = 0;
            if (hostAddress !=  null) {
                for (int i=0; i<instanceList.size(); i++) {
                    if (instanceList.get(i).split("-")[1].equals(hostAddress)) {
                        instanceNo = i;
                    }
                }
            } else {
                logger.info("获取本地IP失败");
            }
            logger.info("[分发] 实例总数={}, 我是第{}个实例", instanceCount, instanceNo);
            List<CheckTask> waitingTasks = checkTaskDao.getTasks(taskType, TaskStatus.WAITING.getValue());
            Iterator<CheckTask> waitingIterator = waitingTasks.iterator();
            while (waitingIterator.hasNext()) {
                if (waitingIterator.next().getTaskId().hashCode() % instanceCount != instanceNo) {
                    waitingIterator.remove();
                }
            }

    说明:

    1、例如有3个实例(zkClient.getInstances()),那么通过IP我们把3个实例按照自增id排序分别标号为0,1,2

    2、对第一个实例也就是instanceNo=0,只执行taskId.hashCode() % 3 == 0的任务,其他两个实例类似

    3、当有一个实例挂掉,2个实例,instanceNo=0只执行taskId.hashCode() % 2 == 0的任务,实现热删除

  • 相关阅读:
    VMware Workstation 11 安装MAC OS X 10.10 Yosemite(14B25)图解 2015-01-13 12:26:01|
    tensor搭建--windows 10 64bit下安装Tensorflow+Keras+VS2015+CUDA8.0 GPU加速
    vs2015终于配置完成了
    Visual Studio 2015 update 3各版本下载地址
    惊艳的cygwin——Windows下的Linux命令行环境的配置和使用
    TensorFlow从入门到实战资料汇总 2017-02-02 06:08 | 数据派
    官方Caffe-windows 配置与示例运行
    ipython notebook 如何打开.ipynb文件?
    Ubuntu16.04 +cuda8.0+cudnn+caffe+theano+tensorflow配置明细
    【CUDA】CUDA开发环境搭建
  • 原文地址:https://www.cnblogs.com/kangoroo/p/7197282.html
Copyright © 2011-2022 走看看