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

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:,10.93.18.34:,10.93.18.35:
zookeeper.sessionTimeoutMs=
zookeeper.connectionTimeoutMs=
zookeeper.maxRetries=
zookeeper.baseSleepTimeMs=

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可以实现锁和负载均衡,下面再说

、PERSISTENT

持久化目录节点,存储的数据不会丢失。

、PERSISTENT_SEQUENTIAL

顺序自动编号的持久化目录节点,存储的数据不会丢失,并且根据当前已近存在的节点数自动加 ,然后返回给客户端已经成功创建的目录节点名。

、EPHEMERAL

临时目录节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除。 

、EPHEMERAL_SEQUENTIAL

临时自动编号节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除,并且根据当前已近存在的节点数自动加 ,然后返回给客户端已经成功创建的目录节点名。

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的任务,实现热删除

springboot使用zookeeper(curator)实现注册发现与负载均衡的更多相关文章

  1. 基于gRPC的注册发现与负载均衡的原理和实战

    gRPC是一个现代的.高性能.开源的和语言无关的通用RPC框架,基于HTTP2协议设计,序列化使用PB(Protocol Buffer),PB是一种语言无关的高性能序列化框架,基于HTTP2+PB保证 ...

  2. 分布式应用开发 | SpringBoot+dubbo+zookeeper实现服务注册发现 | 远程服务调用

    前言 通过新建两个独立服务--提供者.消费者,模拟两个独立分布的应用,通过使用dubbo+zookeeper来实现远程服务调用. 目录 项目搭建 provider-server consumer-se ...

  3. 服务发现与负载均衡 dubbo zk原理

    服务发现与负载均衡 拓展阅读 : dubbo 原理概念图 2016-03-03 杜亦舒 性能与架构 性能与架构 性能与架构 微信号 yogoup 功能介绍 网站性能提升与架构设计 内容整理自文章“实施 ...

  4. grpc服务发现与负载均衡

    前言 在后台服务开发中,高可用性是构建中核心且重要的一环.服务发现(Service discovery)和负载均衡(Load Balance)一直都是我关注的话题.今天来谈一下我在实际中是如何理解及落 ...

  5. marathon的高可用服务自动发现和负载均衡

    上一篇我们说谈了docker+zookeeper+mesos+marathon集群,本篇我们来谈谈marathon的集群和自动发现服务. marathon的服务自动发现和负载均衡有两种,1是mesos ...

  6. 基于marathon-lb的服务自发现与负载均衡

    参考文档: Marathon-lb介绍:https://docs.mesosphere.com/1.9/networking/marathon-lb/ 参考:http://www.cnblogs.co ...

  7. (转) Docker - Docker1.12服务发现,负载均衡和Routing Mesh

    看到一篇介绍 Docker swarm以及如何编排的好文章,挪放到这里,自己学习的同时也分享出来. 原文链接: http://wwwbuild.net/dockerone/414200.html -- ...

  8. 从零开始入门 | Kubernetes 中的服务发现与负载均衡

    作者 | 阿里巴巴技术专家  溪恒 一.需求来源 为什么需要服务发现 在 K8s 集群里面会通过 pod 去部署应用,与传统的应用部署不同,传统应用部署在给定的机器上面去部署,我们知道怎么去调用别的机 ...

  9. Kubernetes 中的服务发现与负载均衡

    原文:https://www.infoq.cn/article/rEzx9X598W60svbli9aK (本文转载自阿里巴巴云原生微信公众号(ID:Alicloudnative)) 一.需求来源 为 ...

随机推荐

  1. group by 详解

    1.概述 2.原始表 3.简单Group By 4.Group By 和 Order By 5.Group By中Select指定的字段限制 6.Group By All 7.Group By与聚合函 ...

  2. Windbg DUMP

    Windbg DUMP分析(原创汇总) 1. 引入篇 1.1 下载安装 1.2 调试器 1.3 操作界面2. 命令篇 2.1 按照来源划分 2.1.1 基本命令 2.1.2 元命令 2.1.3 扩展命 ...

  3. 轻谈 return i++

    在写函数的时候,发现了又一个很有意思的事情 先上代码 public class Test{ static int number = 2; public static void main(String ...

  4. GUI(国际象棋棋盘)

    package com.niit.javagui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridB ...

  5. 201521123098 《Java程序设计》 第5周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 1. 对接口这一定义有了初步的了解: 2. 学习了如何定义实现类和如何实现一些接 ...

  6. 第2周作业-Java基本语法与类库

    1. 本周学习总结 答:① 定义流程控制的各种条件式是同以前学习的一样,要善于运用快捷键.(例如`a/t` + `/` ) ② 熟悉输入输出的使用,注意输入的变量类型使用相应的输入类. ③ 数组对象: ...

  7. 多线程面试题系列(7):经典线程同步 互斥量Mutex

    前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...

  8. webservice05#soap消息

    1, SOAPMessage结构图 2, SOAP消息的创建 1>前面的一个简单WebService  服务 package com.yangw.soap.service; import jav ...

  9. 框架应用:Mybatis(二) - 动态SQL

    MybatisUtil工具类 在实际开发中,我们可以编写一个MybatisUtil辅助类来进行对进行操作. 1)在静态初始化块中加载mybatis配置文件和StudentMapper.xml文件一次 ...

  10. spring依赖注入中接口的问题

    问题描述:一个接口,有俩个实现类当注入时候名字不同时,会出现不同的情况 action层: @Controller("userAction") @Scope("protot ...