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)) 一.需求来源 为 ...
随机推荐
- JS自定义对象以及相关成绩系统完整案例演示
[自定义对象] 1.基本概念 ①对象是拥有一系列无无序属性和方法的集合 ②键值对:对象中的数据,用以键值对的形式存在,对象的每个属性和方法,都对应一个键值,以键取值 ③属性:描述对象特征的一系列变量称 ...
- Java中equals和==之间的区别
今天在写表达式求值的时候,发现了equals和==||!=和!equals()之间是不一样的. 我就从网上搜了搜关于这方面的知识,然后在下面做一个总结: Java中有两类数据类型: 基本数据类型(Pr ...
- 团队作业4--第一次项目冲刺(Alpha版本)7
一.Daily Scrum Meeting照片 二.燃尽图 三.项目进展 1.完成全部基础功能 2.完成一些小改进与优化 四.困难与问题 软件基本是可以运行并且正常使用,但还没有实战过,遇到的问题与困 ...
- 201521123065 《Java程序设计》第4周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容 类设计:属性设计类型为priate并初始化. 文档注释:以/*开始,以*/结束. 继承:存在的 ...
- 第2周作业-Java基本语法与类库
1. 本周学习总结 答:① 定义流程控制的各种条件式是同以前学习的一样,要善于运用快捷键.(例如`a/t` + `/` ) ② 熟悉输入输出的使用,注意输入的变量类型使用相应的输入类. ③ 数组对象: ...
- 201521123003《Java程序设计》第2周学习总结
1. 本章学习总结 你对于本章知识的学习总结 学习了java中各种数据类型的使用 掌握了基本类型的转换 了解string和stringbuilder的区别以及字符串池的原理 学会了使用package管 ...
- 201521123071《Java程序设计》第1周学习总结
1. 本章学习总结 通过本周的学习,对java的一些语法以及java的发展史有了一定的基础认识,也了解了JDK的安装,以及环境变量定义和配置等知识.还有对码云,Markdown等的使用,大大方便了我们 ...
- evak购物车--课程设计(201521123037邱晓娴)
1. 团队课程设计博客链接 团队博客 2. 个人负责模块或任务说明 1.Java (1)编写用户类Users (2)编写DBConnection类,连接数据库 (3)编写GoodsDAO类,从数据库中 ...
- 201521123011 《Java程序设计》第13周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 协议.IP.域名.端口: 常用应用层协议:http, ftp 域名(通过域名可以找到IP)用ping测试 ...
- python之进程----Queue
一.Queue是通过multiprocessing使用 from multiprocessing import Process,Queue import time import random impo ...