简介
集群机器id
myid,它是每一个集群机器中的编号文件,代表 ZooKeeper 集群服务器的标识,手动生成,全局全一
事务 ID
zxid,Zookeeper 会给每个更新请求分配一个事务 ID,它是一个 64 位的数字,由 Leader 统一进行分配,全局唯一,不断递增,在一个节点的状态信息中可以查看到最新的事务 ID 信息。
集群服务器角色
- Leader:主
- Follower:从
- Observer:观察者
服务器状态
- LOOKING:寻找Leader,当前状态没有Leader,正在寻找Leader
- FOLLOWING:跟随者状态,当前服务器是Follower
- LEADING:领导者状态,当前服务器是Leader
- OBSERVERING:观察者状态,当前服务器的角色是Observer
选举方式
- LeaderElection
- AuthFastLeaderElection
- FastLeaderElection(最新默认)
选举场景
- Zookeeper集群启动初始化
- Leader失联重新选举
选举条件
- Zookeeper服务器处于LOOKING竞选状态
- Zookeeper集群至少3台机(2N+1),集群挂了一半或者一半以上才会失效
选举流程
启动顺序为zk1,zk2,zk3
- 初始化选举
初始投票:每个server都会给自己投一票,myid和zxid,zk(myid,zxid)比如zk1(1,0),zk(2,0),zk(3,0)
同步投票结果:各自的投票结果同步给集群其他服务器
检查投票的有效性:本轮的投票的server是否处于LOOKING状态
处理投票:先检查zxid,较大的服务器优先,zxid相同,myid较大的服务器作为Leader,zk1启动,zk2启动与zk1比较,zk1改成(2,0)
统计投票结果:是否有超过半数的机器的投票结果一样,(2,0)有两票,zk2当选
更改服务器状态:选好了Leader,每个服务器更新自己的状态,zk1->FOLLOWING,zk2->LEADING,zk3->FOLLOWING - 集群重新选举
状态变更:Leader不能用了,所有的Follower从FOLLOWING改成LOOKING,开始新一轮选举
开始投票:
同步投票结果
检查投票的有效性
处理投票
统计投票结果
更改服务器状态
特征
-
顺序一致性: 从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到 ZooKeeper 中去,对于来自客户端的每个更新请求,ZooKeeper 都会分配一个全局唯一的递增编号,这个编号反应了所有事务操作的先后顺序,应用程序可以使用 ZooKeeper 这个特性来实现更高层次的同步原语。 这个编号也叫做时间戳——zxid(Zookeeper Transaction Id)。
-
原子性: 所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用。
-
单一系统映像 : 无论客户端连到哪一个 ZooKeeper 服务器上,其看到的服务端数据模型都是一致的。
-
可靠性: 一旦一次更改请求被应用,更改的结果就会被持久化,直到被下一次更改覆盖。
安装
cd /usr/local
wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.6.2/apache-zookeeper-3.6.2-bin.tar.gz
# 解压zookeeper
tar -zxvf apache-zookeeper-3.6.2-bin.tar.gz
cd apache-zookeeper-3.6.2-bin
cd conf/
# 重命名配置文件zoo_sample.cfg
cp zoo_sample.cfg zoo.cfg
cd ../
# 启动zookeeper
bin/zkServer.sh start
# 停止
bin/zkServer.sh stop
# 查看状态
bin/zkServer.sh status
集群搭建
把zookeeper文件复制为4份,zookeeper01、zookeeper02、zookeeper03、zookeeper-client
- 修改zookeeper01/conf/zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/Users/biostime/Documents/java/zookeeper/zookeeper01/data
dataLogDir=/Users/biostime/Documents/java/zookeeper/zookeeper01/log
clientPort=2181
server.1=10.50.101.123:2287:3387
server.2=10.50.101.123:2288:3388
server.3=10.50.101.123:2289:3389
- 新建zookeeper01/data/myid,内容是1
- 修改zookeeper02/conf/zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/Users/biostime/Documents/java/zookeeper/zookeeper02/data
dataLogDir=/Users/biostime/Documents/java/zookeeper/zookeeper02/log
clientPort=2182
server.1=10.50.101.123:2287:3387
server.2=10.50.101.123:2288:3388
server.3=10.50.101.123:2289:3389
-
新建zookeeper02/data/myid,内容是2
-
修改zookeeper03/conf/zoo.cfg
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/Users/biostime/Documents/java/zookeeper/zookeeper03/data dataLogDir=/Users/biostime/Documents/java/zookeeper/zookeeper03/log clientPort=2183 server.1=10.50.101.123:2287:3387 server.2=10.50.101.123:2288:3388 server.3=10.50.101.123:2289:3389
-
新建zookeeper03/data/myid,内容是3
-
然后启动所有的zookeeper
cd bin/ ./zkServer.sh start
输入jps后,会出现三个
QuorumPeerMain
进程 -
进入zookeeper-client的bin目录下
# 连接到zookeeper01上 ./zkCli.sh -timeout 5000 -server 127.0.0.1:2181
zookeeper监控某个值的变化
pom.xml
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
java代码:
package com.example.zookeeper6001.config;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
public class ZookeeperProSync implements Watcher {
private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
private static ZooKeeper zk = null;
private static Stat stat = new Stat();
public static void main(String[] args) throws Exception {
//zookeeper配置数据存放路径
String path = "/zookeeper6001";
//连接zookeeper并且注册一个默认的监听器
zk = new ZooKeeper("127.0.0.1:2181", 5000, //
new ZookeeperProSync());
//等待zk连接成功的通知
connectedSemaphore.await();
//获取path目录节点的配置数据,并注册默认的监听器
System.out.println(new String(zk.getData(path, true, stat)));
Thread.sleep(Integer.MAX_VALUE);
}
public void process(WatchedEvent event) {
if (KeeperState.SyncConnected == event.getState()) { //zk连接成功通知事件
if (EventType.None == event.getType() && null == event.getPath()) {
connectedSemaphore.countDown();
} else if (event.getType() == EventType.NodeDataChanged) { //zk目录节点数据变化通知事件
try {
System.out.println("配置已修改,新值为:" + new String(zk.getData(event.getPath(), true, stat)));
} catch (Exception e) {
}
}
}
}
}
zookeeper命令
# 创建一个树节点
create /zookeeper6001 zookeeper6001Info
# 获取某个节点的内容
get /zookeeper6001
# 更新某个节点的内容
set /zookeeper6001 zookeeper6001InfoChange
java代码会打印
配置已修改,新值为:zookeeper6001InfoChange
zookeeper+openfeign
provider
- 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>zookeeper-provider-8001</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zookeeper-provider-8001</name>
<description>mi</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
-
ZookeeperProvider8001Application
@EnableDiscoveryClient
-
ProviderController
package com.example.zookeeperprovider8001.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ProviderController { @RequestMapping("/hello") public String hello(){ String res = "ProviderController hello success"; System.out.println(res); return res; } }
-
application.properties
server.port=8001 spring.application.name=service-provider spring.cloud.zookeeper.connect-string=127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
comsumer
- 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>zookeeper-comsumer-8002</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zookeeper-comsumer-8002</name>
<description>mi</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
-
ZookeeperComsumer8002Application
@EnableFeignClients
-
ComsumerService
package com.example.zookeepercomsumer8002.server; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(contextId = "comsumerService", value = "service-provider") public interface ComsumerService { @GetMapping("/hello") String hello(); }
-
ComsumerController
package com.example.zookeepercomsumer8002.controller;
import com.example.zookeepercomsumer8002.server.ComsumerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ComsumerController {
@Autowired
ComsumerService service;
@RequestMapping("/comsumer")
public String comsumer(){
String res = service.hello();
System.out.println(res);
return res;
}
}
-
application.properties
server.port=8002 spring.application.name=service-comsumer feign.client.config.default.connect-timeout=500 feign.client.config.default.read-timeout=500 spring.cloud.zookeeper.connect-string=127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 feign.hystrix.enabled=true
连接到其中一台zookeeper
# 查询所有的
ls /
# [services, zookeeper, zookeeper6001]
# 查询services
ls /services
# [service-comsumer, service-provider]
# 查询service-comsumer
ls /services/service-comsumer
# [7ee01bb1-9e58-4923-b8c8-6dfff5590f85]
# 查询[7ee01bb1-9e58-4923-b8c8-6dfff5590f85]
get /services/service-comsumer/7ee01bb1-9e58-4923-b8c8-6dfff5590f85
# {"name":"service-comsumer","id":"7ee01bb1-9e58-4923-b8c8-6dfff5590f85","address":"10.50.101.123","port":8002,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1","name":"service-comsumer","metadata":{"instance_status":"UP"}},"registrationTimeUTC":1609747347415,"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}}
当停掉provider和comsumer后,过一段时间,zookeeper下的/services节点会自动删除
网友评论