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:,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)实现注册发现与负载均衡的更多相关文章
- 基于gRPC的注册发现与负载均衡的原理和实战
gRPC是一个现代的.高性能.开源的和语言无关的通用RPC框架,基于HTTP2协议设计,序列化使用PB(Protocol Buffer),PB是一种语言无关的高性能序列化框架,基于HTTP2+PB保证 ...
- 分布式应用开发 | SpringBoot+dubbo+zookeeper实现服务注册发现 | 远程服务调用
前言 通过新建两个独立服务--提供者.消费者,模拟两个独立分布的应用,通过使用dubbo+zookeeper来实现远程服务调用. 目录 项目搭建 provider-server consumer-se ...
- 服务发现与负载均衡 dubbo zk原理
服务发现与负载均衡 拓展阅读 : dubbo 原理概念图 2016-03-03 杜亦舒 性能与架构 性能与架构 性能与架构 微信号 yogoup 功能介绍 网站性能提升与架构设计 内容整理自文章“实施 ...
- grpc服务发现与负载均衡
前言 在后台服务开发中,高可用性是构建中核心且重要的一环.服务发现(Service discovery)和负载均衡(Load Balance)一直都是我关注的话题.今天来谈一下我在实际中是如何理解及落 ...
- marathon的高可用服务自动发现和负载均衡
上一篇我们说谈了docker+zookeeper+mesos+marathon集群,本篇我们来谈谈marathon的集群和自动发现服务. marathon的服务自动发现和负载均衡有两种,1是mesos ...
- 基于marathon-lb的服务自发现与负载均衡
参考文档: Marathon-lb介绍:https://docs.mesosphere.com/1.9/networking/marathon-lb/ 参考:http://www.cnblogs.co ...
- (转) Docker - Docker1.12服务发现,负载均衡和Routing Mesh
看到一篇介绍 Docker swarm以及如何编排的好文章,挪放到这里,自己学习的同时也分享出来. 原文链接: http://wwwbuild.net/dockerone/414200.html -- ...
- 从零开始入门 | Kubernetes 中的服务发现与负载均衡
作者 | 阿里巴巴技术专家 溪恒 一.需求来源 为什么需要服务发现 在 K8s 集群里面会通过 pod 去部署应用,与传统的应用部署不同,传统应用部署在给定的机器上面去部署,我们知道怎么去调用别的机 ...
- Kubernetes 中的服务发现与负载均衡
原文:https://www.infoq.cn/article/rEzx9X598W60svbli9aK (本文转载自阿里巴巴云原生微信公众号(ID:Alicloudnative)) 一.需求来源 为 ...
随机推荐
- group by 详解
1.概述 2.原始表 3.简单Group By 4.Group By 和 Order By 5.Group By中Select指定的字段限制 6.Group By All 7.Group By与聚合函 ...
- Windbg DUMP
Windbg DUMP分析(原创汇总) 1. 引入篇 1.1 下载安装 1.2 调试器 1.3 操作界面2. 命令篇 2.1 按照来源划分 2.1.1 基本命令 2.1.2 元命令 2.1.3 扩展命 ...
- 轻谈 return i++
在写函数的时候,发现了又一个很有意思的事情 先上代码 public class Test{ static int number = 2; public static void main(String ...
- GUI(国际象棋棋盘)
package com.niit.javagui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridB ...
- 201521123098 《Java程序设计》 第5周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 1. 对接口这一定义有了初步的了解: 2. 学习了如何定义实现类和如何实现一些接 ...
- 第2周作业-Java基本语法与类库
1. 本周学习总结 答:① 定义流程控制的各种条件式是同以前学习的一样,要善于运用快捷键.(例如`a/t` + `/` ) ② 熟悉输入输出的使用,注意输入的变量类型使用相应的输入类. ③ 数组对象: ...
- 多线程面试题系列(7):经典线程同步 互斥量Mutex
前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...
- webservice05#soap消息
1, SOAPMessage结构图 2, SOAP消息的创建 1>前面的一个简单WebService 服务 package com.yangw.soap.service; import jav ...
- 框架应用:Mybatis(二) - 动态SQL
MybatisUtil工具类 在实际开发中,我们可以编写一个MybatisUtil辅助类来进行对进行操作. 1)在静态初始化块中加载mybatis配置文件和StudentMapper.xml文件一次 ...
- spring依赖注入中接口的问题
问题描述:一个接口,有俩个实现类当注入时候名字不同时,会出现不同的情况 action层: @Controller("userAction") @Scope("protot ...