zk简介:
简介
ZooKeeper是一个开源的分布式协调服务,由雅虎创建,是 Google Chubby
的开源实现。分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅、
负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式
锁和分布式队列等功能
集群角色:
Leader
Follower
Observer
一个 ZooKeeper 集群同一时刻只会有一个 Leader,其他都是 Follower
或 Observer。
会话:
Session 是指客户端会话,一个客户端连接是指客户端和 ZooKeeper
服务器之间的TCP长连接,
从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连
接,客户端能够通过心跳检测和服务器保持有效的会话,也能够向 ZooKeeper
服务器发送请求并接受响应,同时还能通过该连接接收来自服务器的
Watch 事件通知。
数据节点:
ZooKeeper 中的数据节点是指数据模型中的数据单元,称为 ZNode。
ZooKeeper 将所有数据存储在内存中,数据模型是一棵树(ZNode Tree),
由斜杠(/)进行分割的路径,就是一个ZNode,如 /hbase/master,其中
hbase 和 master 都是 ZNode。每个 ZNode 上都会保存自己的数据内容,
同时会保存一系列属性信息。
节点类型:
持久节点
临时节点
版本:
ZooKeeper 的每个 ZNode 上都会存储数据,对应于每个 ZNode,ZooKeeper
都会为其维护一个叫作 Stat 的数据结构,Stat 中记录了这个 ZNode 的三
个数据版本,分别是 version(当前ZNode的版本)、cversion(当前ZNode子
节点的版本)和 aversion(当前 ZNode 的 ACL 版本)
状态信息:
每个 ZNode 除了存储数据内容之外,还存储了 ZNode 本身的一些状态信息。
用 get 命令可以同时获得某个 ZNode 的内容和状态信息。
Zookeeper-特性介绍
最终一致性 可靠性 实时性 等待无关 原子性 顺序性
事务操作
在ZooKeeper中,能改变ZooKeeper服务器状态的操作称为事务操作。
一般包括数据节点创建与删除、数据内容更新和客户端会话创建与失效等操作
。对应每一个事务请求,ZooKeeper 都会为其分配一个全局唯一的事务ID,
用 ZXID 表示,通常是一个64位的数字。每一个 ZXID 对应一次更新操作,
从这些 ZXID 中可以间接地识别出 ZooKeeper 处理这些事务操作请求的
全局顺序
Zookeeper-基本概念:
Watcher
Watcher(事件监听器),是 ZooKeeper 中一个很重要的特性。ZooKeeper允许
用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,ZooKeeper
服务端会将事件通知到感兴趣的客户端上去。该机制是 ZooKeeper 实现分布式协
调服务的重要特性。
Acl ZooKeeper 采用 ACL(Access Control Lists)策略来进行权限控制。
ZooKeeper 定义了如下5种权限:
CREATE: 创建子节点的权限
READ: 获取节点数据和子节点列表的权限
WRITE:更新节点数据的权限
DELETE: 删除子节点的权限
ADMIN: 设置节点ACL的权限
Zookeeper-工作原理:
Zookeeper 的核心是原子广播,这个机制保证了各个Server之间的同步。
实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复
模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab
就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader
的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有
相同的系统状态。
Zookeeper- Server工作状态:
LOOKING LEADING FOLLOWING
Zookeeper- Paxos算法
分布式一致性算法(Consensus Algorithm)是一个分布式计算领域的基础性
问题,其最基本的功能是为了在多个进程之间对某个(某些)值达成一致(强
一致);进而解决分布式系统的可用性问题(高可用)。Paxos是最重要的分
布式一致性算法,很多人都把它作为“分布式一致性协议”的代名词(Mike Burrows,
inventor of the Chubby service at Google, says that“there is only
one consensus protocol, and that’s Paxos”)。
Zookeeper-选举流程(basic paxos):
当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,
恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正
确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一
种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。
同步流程:
选完Leader以后,zk就进入状态同步过程。
Leader等待server连接
Follower连接leader,将最大的zxid发送给leader
Leader根据follower的zxid确定同步点
完成同步后通知follower 已经成为uptodate状态
Follower收到uptodate消息后,又可以重新接受client的请求进行服务了
Zookeeper-Leader工作流程:
恢复数据
维持与Learner的心跳,接收Learner请求并判断Learner的请求消息类型
Learner的消息类型主要有PING消息、REQUEST消息、ACK消息、REVALIDATE
消息,根据不同的消息类型,进行不同的处理
Zookeeper-Follower工作流程
向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息)
接收Leader消息并进行处理
接收Client的请求,如果为写请求,发送给Leader进行投票
返回Client结果
引入jar包:
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cc</groupId>
<artifactId>springboot-zookeeper-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-zookeeper-client</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
测试单机client:
package com.cc.springbootzookeeperclient;
import lombok.extern.slf4j.Slf4j;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.ZkConnection;
import org.apache.zookeeper.CreateMode;
import java.util.List;
/**
* Created by CarlosXiao on 2018/7/28.
*/
@Slf4j
public class ZkClientTest {
public static final String ZK_HOST = "192.168.13.51:2182,192.168.13.51:2183,192.168.13.51:2184,192.168.13.51:2185";
public static void main(String [] args) throws InterruptedException {
// 1、创建链接
ZkClient zkClient = new ZkClient(ZK_HOST);
// ZkClient zkClient = new ZkClient(new ZkConnection(ZK_HOST), 5000);
// 2、创建节点
//zkClient.create("/zk", "test", CreateMode.PERSISTENT);
//zkClient.createPersistent("/zkclient/test", true);
// 修改节点内容
// zkClient.writeData("/zkclient/test", "modify data");
// 3、删除节点
//zkClient.delete("/zk");
// 递归删除
//zkClient.deleteRecursive("/zkclient/test");
//List<String> childrens = zkClient.getChildren("/test");
//log.info("childrens: {}", childrens);
zkClient.subscribeChildChanges("/test", new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
log.info("parent node: {}", parentPath);
log.info("current childs: {}", currentChilds);
}
});
Thread.sleep(3000);
zkClient.createPersistent("/test/a");
Thread.sleep(1000);
zkClient.deleteRecursive("/test");
Thread.sleep(Integer.MAX_VALUE);
}
}
实现通知:
package com.cc.springbootzookeeperclient;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* Created by CarlosXiao on 2018/7/28.
*/
@Slf4j
public class ZookeeperTest implements Watcher{
public static final String ZK_HOST = "192.168.13.51:2182,192.168.13.51:2183,192.168.13.51:2184,192.168.13.51:2185";
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String [] args) throws IOException, InterruptedException, KeeperException {
// 1、 创建会话,
/**
* 1、 链接字符串 192.168.13.51:2182,192.168.13.51:2183
*/
ZooKeeper zooKeeper = new ZooKeeper(ZK_HOST, 5000, new ZookeeperTest());
countDownLatch.await();
// 2. 创建节点
zooKeeper.create("/test", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 3. 设置节点内容
zooKeeper.setData("/test", "bbb".getBytes(), -1);
zooKeeper.create("/test/1", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zooKeeper.create("/test/2", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zooKeeper.create("/test/3", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zooKeeper.create("/test/4", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 5. 获取子节点
List<String> childrens = zooKeeper.getChildren("/test", false);
log.info("childrens: {}", childrens);
// 4. 节点删除
zooKeeper.delete("/test", -1);
Thread.sleep(Integer.MAX_VALUE);
}
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
log.info("--------------------------> 已连接");
countDownLatch.countDown();
}
}
}