ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper包含一个简单的原语集,提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在$zookeeper_home\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。
ZooKeeper是以Fast Paxos算法为基础的,[Paxos 算法](https://baike.baidu.com/item/Paxos 算法)存在活锁的问题,即当有多个proposer交错提交时,有可能互相排斥导致没有一个proposer能提交成功,而Fast Paxos做了一些优化,通过选举产生一个leader (领导者),只有leader才能提交proposer,具体算法可见Fast Paxos。因此,要想弄懂ZooKeeper首先得对Fast Paxos有所了解。
ZooKeeper的基本运转流程:
1、选举Leader。
2、同步数据。
3、选举Leader过程中算法有很多,但要达到的选举标准是一致的。
4、Leader要具有最高的执行ID,类似root权限。
5、集群中大多数的机器得到响应并接受选出的Leader。
选举流程
1、 当leader挂了后,其他非observer的机器变更状态为looking,然后开始选举流程
2、每个server都会发出一个投票信息(myid,zxid)
3、接收来自各个服务器的投票
4、处理投票–判断zxid,大的zxid成为leader,如果zxid相同,就判断myid,myid大的做leader
5、统计投票–半数服务器达成一致意见后,投票结束
6、改变自身的状态–leader为leading,其他服务器为following
https://www.yiibai.com/zookeeper/zookeeper_installation.html
https://blog.csdn.net/zlbdmm/article/details/109669049
zoo.cfg 配置文件说明:
IDEA使用spring initializr创建服务,勾选下图依赖
成功后,完整pom如下
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.4.2version>
<relativePath/>
parent>
<groupId>com.cuixk.microserver.zookeepergroupId>
<artifactId>zookeeper-providerartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>zookeeper-providername>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>2020.0.0spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
project>
服务生产者配置文件
server:
port: 8000
spring:
cloud:
zookeeper:
connect-string: 192.168.20.134:2181
discovery:
enabled: true
application:
name: zookeeper-provider
服务消费者配置文件
server:
port: 8001
spring:
cloud:
zookeeper:
connect-string: ip:2181 #zookeeper服务地址
discovery:
enabled: true #开启服务发现功能
application:
name: zookeeper-consumer #应用ID
服务生产者在启动类上添加@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class ZookeeperProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ZookeeperProviderApplication.class, args);
}
}
服务消费者在启动类上添加@EnableDiscoveryClient,@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ZookeeperConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ZookeeperConsumerApplication.class, args);
}
}
启动项目,如下图所示即注册成功
通用pojo
@Data
public class User {
private Integer id;
private String name;
private Integer age;
}
服务生产者
controller代码
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public User getUserInfo(@PathVariable Integer id) {
User user = new User();
user.setId(id);
user.setName("provider");
user.setAge(25);
return user;
}
}
服务消费者
controller代码
@RestController
@RequestMapping("/get")
public class OrderController {
@Resource
private UserFeign userFeign;
@GetMapping("/{id}")
public String getUserInfo(@PathVariable Integer id) {
User userInfo = userFeign.getUserInfo(id);
return userInfo.toString();
}
}
feign代码
@FeignClient("zookeeper-provider") //此处填写服务生产者注册的serviceName
public interface UserFeign {
@GetMapping("/user/{id}")
User getUserInfo(@PathVariable Integer id);
}
调用消费者接口,若有结果返回,则测试通过。
https://blog.csdn.net/u010842515/article/details/51147016
@Autowired
private DiscoveryClient discoveryClient;
public String serviceUrl() {
List<ServiceInstance> list = discoveryClient.getInstances("STORES");
if (list != null && list.size() > 0 ) {
return list.get(0).getUri().toString();
}
return null;
}
原因:
1.discoveryClient.getInstances(“STORES”); 此处入参需改成实际注册的serviceName
2.需开启服务发现注解 @EnableDiscoveryClient
https://blog.csdn.net/zlbdmm/article/details/109669049
由上述链接集群搭建成功后,修改配置文件
connect-string 配置了所有zk节点,原因并不是为了向所有zk注册信息,而是如果 zk1挂掉之后,转向其他zk注册,保证服务可以注册成功
server:
port: 8001
spring:
cloud:
zookeeper:
connect-string: localhost:12181,localhost:12182,localhost:12183
discovery:
enabled: true
application:
name: zookeeper-consumer
server:
port: 8081
spring:
application:
name: service-provider
cloud:
nacos:
discovery:
server-addr: 192.168.20.134:8848
进行第 4 节的测试工作。
1、容错
由于在增删改操作中需要半数以上服务器通过,来分析以下情况。
2台服务器,至少2台正常运行才行(2的半数为1,半数以上最少为2),正常运行1台服务器都不允许挂掉
3台服务器,至少2台正常运行才行(3的半数为1.5,半数以上最少为2),正常运行可以允许1台服务器挂掉
4台服务器,至少3台正常运行才行(4的半数为2,半数以上最少为3),正常运行可以允许1台服务器挂掉
5台服务器,至少3台正常运行才行(5的半数为2.5,半数以上最少为3),正常运行可以允许2台服务器挂掉
6台服务器,至少3台正常运行才行(6的半数为3,半数以上最少为4),正常运行可以允许2台服务器挂掉
通过以上可以发现,3台服务器和4台服务器都最多允许1台服务器挂掉,5台服务器和6台服务器都最多允许2台服务器挂掉
但是明显4台服务器成本高于3台服务器成本,6台服务器成本高于5服务器成本。这是由于半数以上投票通过决定的。
2、防脑裂
一个zookeeper集群中,可以有多个follower、observer服务器,但是必需只能有一个leader服务器。
如果leader服务器挂掉了,剩下的服务器集群会通过半数以上投票选出一个新的leader服务器。
集群互不通讯情况:
一个集群3台服务器,全部运行正常,但是其中1台裂开了,和另外2台无法通讯。3台机器里面2台正常运行过半票可以选出一个leader。
一个集群4台服务器,全部运行正常,但是其中2台裂开了,和另外2台无法通讯。4台机器里面2台正常工作没有过半票以上达到3,无法选出leader正常运行。
一个集群5台服务器,全部运行正常,但是其中2台裂开了,和另外3台无法通讯。5台机器里面3台正常运行过半票可以选出一个leader。
一个集群6台服务器,全部运行正常,但是其中3台裂开了,和另外3台无法通讯。6台机器里面3台正常工作没有过半票以上达到4,无法选出leader正常运行。
通可以上分析可以看出,为什么zookeeper集群数量总是单出现,主要原因还是在于第2点,防脑裂,对于第1点,无非是正本控制,但是不影响集群正常运行。但是出现第2种脑裂的情况,zookeeper集群就无法正常运行了。
https://docs.spring.io/spring-cloud-zookeeper/docs/current/reference/html/ 官方网址
https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.5.9/apache-zookeeper-3.5.9-bin.tar.gz 下载地址
https://www.yiibai.com/zookeeper/zookeeper_installation.html zookeeper安装与启动
https://cloud.tencent.com/developer/article/1505113 服务注册发现之Zookeeper服务注册
https://blog.csdn.net/zlbdmm/article/details/109669049 Zookeeper下载与安装教程(for windows)
https://blog.csdn.net/CSDN_Stephen/article/details/78856323 用Zookeeper作为Spring cloud的配置中心