注:本文篇幅有点长,所以建议各位下载源码学习。(如需要请收藏!转载请声明来源,谢谢!)
什么服务注册中心?
注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就这里找到服务的地址,进行调用。
引用:https://www.cnblogs.com/dalianpai/p/12255458.html
个人理解:注册中心就类似手机公共的通讯录。两部手机,有的用移动卡,有的用联通卡,大家只要注册上去后都可以被其他人查到,然后可以进行互拨。当要拨打的时候从注册中心上面查看到某个人的具体号码(ip地址),然后进行拨打。
服务注册中心的作用?
服务注册中心主要起到了一个"协调者"的作用,主要解决两大问题:服务注册与服务发现;区别于传统直接调服务,你是不清楚该服务是存不存在,现在是否还存活,但是服务注册中心有心跳可以检查该服务是否存活,当某台节点有异常,可以将该请地轮询到其他节点上面。服务注册中心就是协调服务提供/消费者的注册与发现,并且对其进行健康检测,当节点新增或停止根据机制对其进行相应处理,这个区别于传统,某少机器出问题了还未知,不能及时熔断掉。
服务注册中心有哪些功能?
服务注册与发现
将节点注册到服务注册中心供消费者调用;
服务配置
支持配置相关的策略,当然每个服务注册中心不一样全都相同;
服务键康检测
通过心跳检测服务的键康,对已经无法提供服务的节点进行剔除,对新增的节点加入服务列表;
负载均衡
一个服务提供多个子节点,会进行轮询进行分配请求;
优缺点:
优点:
服务调用者与服务提供者解耦;
方便横向收缩,新增/删除节点到服务注册中心只需要注册上就可以被轮询到;
缺点:
使系统更复杂:加上注册中心后,所有的提供或暴露都需要通过注册中心,这样会使代码和系统更加复杂(这个其实还好);
排查bug比较麻烦:排查异常的时候有时候某个节点出现问题比较难发现具体在哪里有问题(有一定经验还好);
什么是CAP?
请参照:什么是CAP理论?
服务注册中心有哪些?
Zookeeper简称zk
由apache开源的一个项目,用于分布式应用程序的分布式,开放源代码协调服务。它公开了一组简单的原语,分布式应用程序可以基于这些原语来实现用于同步,配置维护以及组和命名的更高级别的服务。它的设计易于编程,并使用了按照文件系统熟悉的目录树结构样式设置的数据模型。它以Java运行,并且具有Java和C的绑定。
参考:https://zookeeper.apache.org/
注意:zk是基于CP原则。
Eureka
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。----百度百科
参考:https://baike.baidu.com/item/Eureka/22402835
注意:Eureka 2.x开始不维护了,停止更新
停更声名:https://github.com/Netflix/eureka/wiki
Consul
Consul是HashiCorp公司推出的开源工具,是一个服务网格(微服务间的 TCP/IP,负责服务之间的网络调用、限流、熔断和监控)解决方案,它是一个一个分布式的,高度可用的系统,而且开发使用都很简便。它提供了一个功能齐全的控制平面,主要特点是:服务发现、健康检查、键值存储、安全服务通信、多数据中心。
参考:
https://kingfree.gitbook.io/consul/
https://www.consul.io/
https://www.jianshu.com/p/7d20dc58c9fc
Nacos
nacos是阿里巴巴开源的一个分布式注册/配置中心,提供服务发现和服务健康监测、动态配置服务、动态 DNS 服务、服务及其元数据管理。
参考:
https://nacos.io/zh-cn/docs/what-is-nacos.html
代码实现
Zookeeper相关实现
zk相关配置
下载地址:https://zookeeper.apache.org/
配置也可以参照:https://zookeeper.apache.org/doc/current/zookeeperStarted.html
下载后->解压->进入conf->复制一份zoo_sample.cfg 并且改名为zoo.cfg
编辑zoo.cfg 修改如下,注意这里的路劲是你自的路劲
dataDir=D:\\tools\\apache-zookeeper-3.6.2-bin\\data
dataLogDir=D:\\tools\\apache-zookeeper-3.6.2-bin\\log
保存后运行zkServer.cmd
配置完毕~
java实现zk连接
引入zk包
<dependencies>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.4-beta</version>
</dependency>
</dependencies>
/**
* @author: csh
* @Date: 2020/12/11 12:01
* @Description:测试zk创建新节点
*/
public class ZkSimpleTest{
public static void main(String[] args) throws Exception {
ZooKeeper zk = new ZooKeeper("127.0.0.1:2181",30000,new Student());
String node = "/hongNode";
Stat stat = zk.exists(node,false);
if(null==stat){
//新建节点
String createResult = zk.create(node,"hong".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(createResult);
}
byte[] b = zk.getData(node,false,stat);
System.out.println(new String(b));
zk.close();
}
}
--------------------
path:null
type:None
state:SyncConnected
--------------------
/hongNode
hong
工具类
package com.hong.zk;
import com.hong.entity.Student;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import java.util.List;
import java.util.Scanner;
/**
* @author: csh
* @Date: 2020/12/11 12:01
* @Description:测试zk创建新节点
*
* create : 在树中的某个位置创建一个节点
* delete : 删除节点
* exists : 测试某个节点是否存在于某个位置
* get data : 从节点读取数据
* set data : 将数据写入节点
* get children : 检索节点的子节点列表
* sync : 等待数据传播
*/
public class ZkClientTest {
static ZooKeeper zk = null;
public static void main(String[] args) throws Exception {
System.out.println("输入:1创建节点,2删除节点,3查询节点所有子节点,9退出");
Scanner scanner = new Scanner(System.in);
Integer inNumber = 1;
try {
zk = new ZooKeeper("127.0.0.1:2181",30000,new Student());
while (inNumber!=0 && inNumber!=9){
inNumber = scanner.nextInt();
switch (inNumber){
case 1:
System.out.println("输入要创建节点的名称!");
scanner = new Scanner(System.in);
create(scanner.next());
continue;
case 2:
System.out.println("输入要删除节点的名称!");
scanner = new Scanner(System.in);
delete(scanner.next());
continue;
case 3:
System.out.println("查询节点所有子节点");
scanner = new Scanner(System.in);
prinChild(scanner.next());
continue;
default:
break;
}
}
System.out.println("结束!");
}catch (Exception e){
e.printStackTrace();
}finally {
if(zk!=null){
zk.close();
}
}
}
//创建节点
public static void create(String node){
if(!valid(node)){
return;
}
try {
Stat stat = zk.exists(node,false);
if(null==stat){
//新建节点
String createResult = zk.create(node,"hong".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(createResult);
}
byte[] b = zk.getData(node,false,stat);
System.out.println(new String(b));
}catch (Exception e){
e.printStackTrace();
}
}
/**
*
* 功能描述: 删除
*
* @param:
* @return:
* @auther: csh
* @date: 2020/12/11 17:44
*/
public static void delete(String node){
if(!valid(node)){
return;
}
try {
Stat stat = zk.exists(node,false);
if(null==stat){
System.out.println("输入的值不存在!");
return;
}
//-1表示任何版本
zk.delete(node,-1);
}catch (Exception e){
e.printStackTrace();
}
}
public static void prinChild(String node){
if(!valid(node)){
return;
}
try {
Stat stat = zk.exists(node,false);
if(null!=stat){
List <String> children = zk.getChildren(node, true);
System.out.println(children);
}else{
System.out.println("没有获取到内容!");
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
*
* 功能描述: 校验
*
* @param:
* @return:
* @auther: csh
* @date: 2020/12/11 17:37
*/
private static boolean valid(String val){
if(val==null||val==""){
System.out.println("输入的值为空!");
return false;
}
return true;
}
}
通过客户端查询创建节点,运行zkCli.cmd
获取节点列表
ls /
获取获点值
get /hongNode
删除节点
delete /hongNode
创建节点
set /hongNode hong1
create /hongNode
设置节点值
注意:运行过程中注意关闭防火墙 不则报以下问题!
org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss for /hongNode2
at org.apache.zookeeper.KeeperException.create(KeeperException.java:102)
at org.apache.zookeeper.KeeperException.create(KeeperException.java:54)
at org.apache.zookeeper.ZooKeeper.exists(ZooKeeper.java:1933)
at org.apache.zookeeper.ZooKeeper.exists(ZooKeeper.java:1961)
at com.hong.zk.ZkClientTest.main(ZkClientTest.java:28)
spring实现zk连接,客户端
jdbc.properties
config.properties:
#数据库驱动
jdbc.driver=com.mysql.jdbc.Driver
#数据库连接url
jdbc.url=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8
#数据库用户名
jdbc.user=root
#数据库密码
jdbc.password=123456
#zk注册地址
zk.address=127.0.0.1:2181
#zk超时
zk.session.timeout=30000
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 配置组件扫描 -->
<context:component-scan base-package="com.hong.spring"></context:component-scan>
<!--加载配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--<context:property-placeholder location="classpath:zk.properties"/>-->
<!-- 开启注解 -->
<context:annotation-config />
<!--开启注解事务-->
<tx:annotation-driven transaction-manager="transactionManager" />
<!--放行静态资源-->
<mvc:default-servlet-handler />
<bean id="zkWatchend" class="com.hong.spring.config.ZkWatchend" />
<bean class="org.apache.zookeeper.ZooKeeper" id="zooKeeper">
<constructor-arg name="connectString" value="${zk.address}"/>
<constructor-arg name="sessionTimeout" value="${zk.session.timeout}"/>
<constructor-arg name="watcher" ref="zkWatchend"/>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/pages/" />
<!-- 后缀 -->
<property name="suffix" value=".html" />
<property name="contentType" value="text/html"/>
</bean>
<!--开启mvc注解事务-->
<!-- 定义注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 设置支持中文 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 基础配置 -->
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<!-- 关键配置 -->
<!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 -->
<property name="initialSize" value="3" />
<!-- 最小连接池数量 -->
<property name="minIdle" value="2" />
<!-- 最大连接池数量 -->
<property name="maxActive" value="15" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="10000" />
<!-- 性能配置 -->
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<!-- 其他配置 -->
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<!-- 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,
执行validationQuery检测连接是否有效。-->
<property name="testWhileIdle" value="true" />
<!-- 这里建议配置为TRUE,防止取到的连接不可用 ,申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。-->
<property name="testOnBorrow" value="true" />
<!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
<property name="testOnReturn" value="false" />
</bean>
<!--事务管理器-->
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<!-- 加载 MyBatis 的配置文件 -->
<property name="configLocation" value="classpath:mybatis-plus.xml"/>
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 所有配置的mapper文件 -->
<property name="mapperLocations" value="classpath*:com/hong/spring/mapper/*.xml" />
</bean>
<!-- Mapper 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描 包下的组件 -->
<property name="basePackage" value="com.hong.spring.dao" />
<!-- 关联mapper扫描器 与 sqlsession管理器 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!--事务配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring_registration_center</artifactId>
<groupId>com.hong</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.hong</groupId>
<artifactId>spring_zk_client</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<logback.version>1.2.3</logback.version>
<over-slf4j.version>1.7.25</over-slf4j.version>
<spring.version>4.3.11.RELEASE</spring.version>
<commons-dbcp.version>1.4</commons-dbcp.version>
<slf4j.version>1.7.12</slf4j.version>
<org.mybatis>3.1.0</org.mybatis>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${org.mybatis}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${org.mybatis}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework</groupId>-->
<!--<artifactId>spring-jdbc</artifactId>-->
<!--<version>${spring.version}</version>-->
<!--</dependency>-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
<!--引入连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<!--引入AOP-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!--该包的主要作用是会去自动查找合适的日志记录框架进行记录-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--引入日志-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
<optional>true</optional>
</dependency>
<!-- 实现slf4j接口并整合 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.2</version>
</dependency>
<!--引入hibernate-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.2.0.Final</version>
</dependency>
<!--引入orm-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.4-beta</version>
</dependency>
</dependencies>
<!--静态资源导出问题-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<!--开启编译调试信息的开关-->
<debug>true</debug>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
com.hong.spring.controller.ZkController
package com.hong.spring.controller;
import lombok.extern.log4j.Log4j2;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author: csh
* @Date: 2020/12/11 18:51
* @Description:
*/
@RestController
@RequestMapping("/zk")
@Log4j2
public class ZkController {
@Autowired
private ZooKeeper zk;
/**
*
* 功能描述: 添加
*
* @param:
* @return:
* @auther: csh
* @date: 2020/12/11 19:16
*/
@RequestMapping("/add")
public String add(String node){
if(StringUtils.isEmpty(node)){
log.error("参数异常!");
return "参数异常!";
}
try {
node="/"+node;
Stat stat = zk.exists(node,false);
if(null==stat){
//新建节点
String createResult = zk.create(node,"hong".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(createResult);
}
byte[] b = zk.getData(node,false,stat);
log.info(new String(b));
}catch (Exception e){
e.printStackTrace();
return "失败";
}
return "成功";
}
/**
*
* 功能描述: 设置
*
* @param:
* @return:
* @auther: csh
* @date: 2020/12/11 19:17
*/
@RequestMapping("/set")
public String add(String node,String val){
if(StringUtils.isEmpty(node)){
log.error("参数异常!");
return "参数异常!";
}
try {
node ="/"+node;
Stat stat = zk.exists(node,true);
if(null==stat){
//新建节点
log.error("设置失败!");
return "设置失败!";
}
zk.setData(node, val.getBytes(), -1);
byte[] b = zk.getData(node,true,stat);
log.info(new String(b));
return new String(b);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
注:这里仅贴核心配置,其他请拉项目参考
测试
http://localhost:8088/zk/add?node=hong34
http://localhost:8088/zk/set?node=hong34&val=1111111111111
springboot实现zk连接,客户端
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot_all</artifactId>
<groupId>com.hong.springboot</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.hong.springboot</groupId>
<artifactId>springboot_zk_client</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
#端口
server.port=8089
#集群的话,地址填写为127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
zookeeper.address = 127.0.0.1:2181
zookeeper.timeout = 10000
com.hong.config.ZkConfig
package com.hong.config;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.CountDownLatch;
/**
* @author: csh
* @Date: 2020/12/14 11:19
* @Description:zk配置
*/
@Configuration
public class ZkConfig {
private static final Logger logger = LoggerFactory.getLogger(ZkConfig.class);
@Value("${zookeeper.address}")
private String connectString;
@Value("${zookeeper.timeout}")
private int timeout;
@Bean(name = "zkClient")
public ZooKeeper zkClient() {
ZooKeeper zooKeeper = null;
try {
CountDownLatch countDownLatch = new CountDownLatch(1);
zooKeeper = new ZooKeeper(connectString, timeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if(Event.KeeperState.SyncConnected==watchedEvent.getState()){
//如果收到了服务端的响应事件,连接成功
countDownLatch.countDown();
}
}
});
countDownLatch.await();
}catch (Exception ex)
{
logger.error("初始化ZooKeeper连接异常....】={}",ex);
}
return zooKeeper;
}
}
com.hong.service.IZkService
/**
* @author: csh
* @Date: 2020/12/14 11:20
* @Description:zk服务配置接口
*/
public interface IZkService {
/**
* 判断指定节点是否存在
* @param path
* @param needWatch 指定是否复用zookeeper中默认的Watcher
* @return
*/
Stat exists(String path, boolean needWatch);
/**
* 检测结点是否存在 并设置监听事件
* 三种监听类型:创建,删除,更新
*
* @param path
* @param watcher 传入指定的监听类
* @return
*/
Stat exists(String path, Watcher watcher );
/**
* 创建持久化节点
* @param path
* @param data
*/
boolean createNode(String path, String data);
/**
* 修改持久化节点
* @param path
* @param data
*/
boolean updateNode(String path, String data);
/**
* 删除持久化节点
* @param path
*/
boolean deleteNode(String path);
/**
* 获取当前节点的子节点(不包含孙子节点)
* @param path 父节点path
*/
List<String> getChildren(String path) throws KeeperException, InterruptedException;
/**
* 获取指定节点的值
* @param path
* @return
*/
String getData(String path, Watcher watcher);
}
com.hong.service.impl.ZkServiceImpl
package com.hong.service.impl;
import com.hong.service.IZkService;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author: csh
* @Date: 2020/12/14 11:21
* @Description:zk相关方法实现
*/
@Service
@Component
public class ZkServiceImpl implements IZkService {
private static final Logger logger = LoggerFactory.getLogger(ZkServiceImpl.class);
@Autowired
private ZooKeeper zkClient;
@Override
public Stat exists(String path, boolean needWatch) {
try {
return zkClient.exists(path,needWatch);
} catch (Exception e) {
logger.error("【断指定节点是否存在异常】{},{}",path,e);
return null;
}
}
@Override
public Stat exists(String path, Watcher watcher) {
try {
return zkClient.exists(path,watcher);
} catch (Exception e) {
logger.error("【断指定节点是否存在异常】{},{}",path,e);
return null;
}
}
@Override
public boolean createNode(String path, String data) {
try {
zkClient.create(path,data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
return true;
} catch (Exception e) {
logger.error("【创建持久化节点异常】{},{},{}",path,data,e);
return false;
}
}
@Override
public boolean updateNode(String path, String data) {
try {
//zk的数据版本是从0开始计数的。如果客户端传入的是-1,则表示zk服务器需要基于最新的数据进行更新。如果对zk的数据节点的更新操作没有原子性要求则可以使用-1.
//version参数指定要更新的数据的版本, 如果version和真实的版本不同, 更新操作将失败. 指定version为-1则忽略版本检查
zkClient.setData(path,data.getBytes(),-1);
return true;
} catch (Exception e) {
logger.error("【修改持久化节点异常】{},{},{}",path,data,e);
return false;
}
}
@Override
public boolean deleteNode(String path) {
try {
//version参数指定要更新的数据的版本, 如果version和真实的版本不同, 更新操作将失败. 指定version为-1则忽略版本检查
zkClient.delete(path,-1);
return true;
} catch (Exception e) {
logger.error("【删除持久化节点异常】{},{}",path,e);
return false;
}
}
@Override
public List<String> getChildren(String path) throws KeeperException, InterruptedException {
List<String> list = zkClient.getChildren(path, false);
return list;
}
@Override
public String getData(String path, Watcher watcher) {
try {
Stat stat=new Stat();
byte[] bytes=zkClient.getData(path,watcher,stat);
return new String(bytes);
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
com.hong.service.impl.WaterServiceImpl
package com.hong.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.springframework.stereotype.Service;
/**
*
* 功能描述: zk监听
*
* @param:
* @return:
* @auther: csh
* @date: 2020/12/14 14:21
*/
@Service
@Slf4j
public class WaterServiceImpl implements Watcher {
@Override
public void process(WatchedEvent event) {
log.info("【Watcher监听事件】={}",event.getState());
log.info("【监听路径为】={}",event.getPath());
log.info("【监听的类型为】={}",event.getType()); // 三种监听类型:创建,删除,更新
}
}
com.hong.Application
package com.hong;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
* 功能描述: zk客户端
*
* @param:
* @return:
* @auther: csh
* @date: 2020/12/14 11:30
*/
@SpringBootApplication(scanBasePackages = "com.hong")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
运行结果
修改:http://localhost:8089/getdata?path=hong34
修改:http://localhost:8089/updateNode?path=hong34&data=2222222222222222
查看值:
相对来说spring boot集成zk比mvc好集成多了。有没有发现其实zk也是可以做分布式配置中心的,当然也可以通过zk来实现分布式锁,相对于redis,zk实现会比较繁琐。这里不列举~,注意:实际工作中,zk是以集群方式搭建。
Eureka
由于停更,现在的组件更新这么快,并且该组件只能在spring cloud上面使用,如果有漏洞或者其他问题,没有维护个人觉得没有太大的实现价值,当然原有用到的可以参考其他技术文章;
https://www.cnblogs.com/jpfss/p/11314673.html
最后
由于考虑篇幅,其它注册中心放到下篇文章统一输出!如果疑问和问题留私聊或留言,谢谢!
参考文章:
https://www.cnblogs.com/dalianpai/p/12255458.html
http://www.fengzhihai.cn/2020/03/31/%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E5%AF%B9%E6%AF%94/
https://www.jianshu.com/p/5014bb302c7d
https://jishuin.proginn.com/p/763bfbd31b0e
https://blog.csdn.net/u013938578/article/details/104119520