springboot使用zookeeper(curator)实现注册发现与负载均衡的方法
创始人
2024-11-17 06:42:52
0次

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

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

2、负载均衡

3、热删除

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

 

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

  
            org.apache.zookeeper
            zookeeper
            3.4.8
        
        
            org.apache.curator
            curator-recipes
            2.9.1
        

        
            org.apache.curator
            curator-client
            2.9.1  

 

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 getChildren(String path) {
        List 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 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;

@Configurationpublic 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 instanceList = zkClient.getInstances();
        Collections.sort(instanceList);
        String hostAddress = NetFunction.getAddressHost();        int instanceNo = 0;        if (hostAddress !=  null) {            for (int i=0; i waitingTasks = checkTaskDao.getTasks(taskType, TaskStatus.WAITING.getValue());
        Iterator 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的任务,实现热删除

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...