文章目录
zookeeper诞生背景和基于Curator的基本操作
一、包含的知识点
- zookeeper出现的背景
- zookeeper核心知识点
- zookeeper包含的特性
- zookeeper基于Curator的实践操作
二、zookeeper出现的背景
在讲解zookeeper之前, 先了解Google Chubby的作用, Chubby是用来解决分布式一致性问题的组件, 同时也是粗粒度的分布式锁服务。关于Google Chubby更详细内容可以根据对应链接进行查看 。Chubby提供的功能:
- 解决分布式一致性问题
- 粗力度分布式锁
2.1 分布式一致性问题
互联网开发过程中, 分布式系统必然存在多个节点, 以投票操作为例, 如果需要达到一致性结果, 需要满足
-
每个节点都会提出请求,
-
所有节点请求只有一个能通过,
-
这个结果对所有节点都是一致的。
但是实际环境可能存在下面的问题:
- 通信异常
- 节点和节点之间存在网络不可用、消息延时、消息丢失问题
- 网络分区
- 服务组内通信正常, 组间通信异常, 通常提到的 “脑裂” 现象
- 节点故障
- 节点出现宕机、器件损坏
- 请求三态
- 节点请求存在成功、失败、超时, 如果请求超时, 请求者无法知道是否成功处理
其实这就是常说的**拜占庭将军问题**, 即如何在网络通信不可靠、消息可能丢失、网络延迟背景下对某个请求达成一致。
那Chubby实现原理是怎样的呢 ?
在进行选举投票阶段, 所有的节点Server通过Chubby提供的通信协到Chubby Server创建同一个文件, 最终只有一个文件能够获准创建这个文件, 创建成功的Server成为master, 并在这个文件中写入自己相关信息, 其它节点通过读取这个文件获取master地址信息。
2.2 粗力度分布式锁
Chubby是通过创建文件来提供锁功能, 节点向Chubby Server创建文件的过程其实就是加锁操作, 文件创建成功表示抢占到了锁。而Chubby没有开源, 雅虎基于Chubby思想研发了zookeeper, 所以作为高可靠的分布式协调中间件zookeeper, 它是Google Chubby的一个开源实现。
注:
1. zookeeper是基于Chubby思想来设计的, 它的作用是实现分布式锁, 注册中心只是它实现的一种功能而已。
2. 本篇文章主要讨论zookeeper相关知识, 对分布式一致性解决方案的具体讨论, 后面会专门整理一篇文章来说明
三、Zookeeper核心知识点
如果需要对zookeeper有深入的了解, 对其相关名词的了解肯定少不了, 下面对zookeeper常见的名词进行整理
-
集群角色
- Leader
- 领导者, 集群只有一个, 处理客户端的写请求, 参与选举
- Follower
- 跟随者, 集群中除Leader外可参与选择的Server, 处理客户端的读请求, 参与选举
- Observer
- 观察者, 处理客户端的读请求, 不参与选举
- Leader
-
ZNode
- zookeeper的视图结构和标准文件系统类似, 每个节点称之为ZNode, 是zookeeper的最小单元, 每个节点可以保存数据以及挂载子节点
-
ZNode分类
- 持久节点(
PERSISTENT
)- 当客户端失去连接时, znode不会自动删除
- 持久有序节点(
PERSISTENT_SEQUENTIAL
)- 当客户端失去连接时, znode不会自动删除
- 节点以单调递增的方式命名
- 临时节点(
EPHEMERAL
)- 当客户端失去连接时, znode会自动删除
- 临时节点(
EPHEMERAL_SEQUENTIAL
)- 当客户端失去连接时, znode会自动删除
- 节点以单调递增的方式命名
- 持久节点(
-
会话Session
- Client初始化连接, 状态转为Connecting
- Client与Server成功建立连接, 状态转为Connected
- Client与Server连接丢失或没有收到Server响应, 状态转为Connecting
- Client主动关闭会话, 状态转为Closed
- 会话过期(Server负责会话过期), 状态转为Closed
-
Stat状态信息
@InterfaceAudience.Public public class Stat implements Record { private long czxid; // 表示该节点被创建时的事务ID private long mzxid; // 表示该节点最后一次被更新时的事务ID private long ctime; // 表示节点被创建时的时间 private long mtime; // 表示节点最后一次被更新时的时间 private int version; // 数据节点版本号 private int cversion; // 子节点版本号 private int aversion; // 子节点ACL版本号 private long ephemeralOwner; // 创建临时结点时的会话sessionId, 如果是持久化结点, 固定值0 private int dataLength; // 数据内容长度 private int numChildren; // 当前结点的子节点个数 private long pzxid; // 表示当前结点的子节点列表最后一次被修改时的事务ID, NOTE: 只有子节点列表变更时才会变更pzxid, 子节点内容变更不会影响pzxid }
-
zookeeper 权限
-
Create 允许对子节点执行创建操作
-
Delete 允许对子节点执行删除操作
-
Read 允许对本节点执行 getChildren 和 getData操作
-
Write 允许对本节点执行 setData操作
-
Admin 允许对本节点执行setAcl操作
-
zookeeper重试策略
-
ExponentialBackoffRetry
: 重试指定次数, 每次重试之间停顿时间增加 -
RetryNTimes
: 指定最大重试次数的重试策略 -
RetryOneTime
: 重试次数只有一次的重试策略 -
RetryUntilElapsed
: 一直重试直到达到指定时间的重试策略 -
zookeeper事件
@InterfaceAudience.Public
public enum EventType {
None (-1), //当zookeeper客户端连接状态发生变更时(KeeperState),该事件被触发
NodeCreated (1), //当结点被创建时, 该事件被触发
NodeDeleted (2), //当结点被删除时, 该事件被触发
NodeDataChanged (3), //当结点的数据发生变更时, 该事件被触发
NodeChildrenChanged (4); //当结点的直接子节点发生创建、删除操作时, 该事件被触发
}
//客户端连接状态
public enum KeeperState {
Unknown (-1),
Disconnected (0),
NoSyncConnected (1),
SyncConnected (3),
AuthFailed (4),
ConnectedReadOnly (5),
SaslAuthenticated(6),
Expired (-112);
}
- Zookeeper watch监听结点变化方式
- PathChildCache
- 监听某路径下子结点创建、删除、更新
- NodeCache
- 监听当前结点的创建、更新、删除, 并将结点数据缓存在本地
- TreeCache
- 同时具有PathChildCache和NodeCahce功能, 监听某路径下创建、删除、更新事件, 并缓存路径下所有结点数据
- PathChildCache
四、zookeeper特性
从第二节、第三节内容了解, zookeeper具有如下特点
- 临时节点
- 持久化节点
- 顺序节点
- 临时节点不能创建子节点
- 同一级节点不能创建相同名称的节点
- TTL和容器节点
五、zookeeper基于Curator的实践操作
5.1 pom文件
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
5.2 增删改查操作
public class CuratorMain {
private static String CONNECTION_STR="127.0.0.1:2181";
public static void main(String[] args) throws Exception {
CuratorFramework curatorFramework= CuratorFrameworkFactory.builder()
.connectString(CONNECTION_STR)
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000,3))
.build();
curatorFramework.start(); //启动
createData(curatorFramework);
// updateData(curatorFramework);
// queryData(curatorFramework);
// deleteData(curatorFramework);
}
// 新增
private static void createData(CuratorFramework curatorFramework) throws Exception {
curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).
forPath("/data/program","test".getBytes());
}
// 更新
private static void updateData(CuratorFramework curatorFramework) throws Exception {
curatorFramework.setData().forPath("/data/program","up".getBytes());
}
private static void queryData(CuratorFramework curatorFramework) throws Exception {
byte[] bytes = curatorFramework.getData().forPath("/data/program");
String data = new String(bytes) ;
System.out.println(data);
}
// 删除
private static void deleteData(CuratorFramework curatorFramework) throws Exception {
Stat stat=new Stat();
String value=new String(curatorFramework.getData().storingStatIn(stat).forPath("/data/curator"));
curatorFramework.delete().withVersion(stat.getVersion()).forPath("/data/curator");
}
}
5.3 zookeeper watch 监听机制
public class WatchMain {
private static String CONNECTION_STR="127.0.0.1:2181";
public static void main(String[] args) throws Exception {
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().
connectString(CONNECTION_STR).sessionTimeoutMs(5000).
retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
curatorFramework.start();
// addListenerWithNode(curatorFramework);
addListenerWithChild(curatorFramework) ;
System.in.read();
}
//配置中心
//创建、修改、删除
private static void addListenerWithNode(CuratorFramework curatorFramework) throws Exception {
NodeCache nodeCache=new NodeCache(curatorFramework,"/watch",false);
NodeCacheListener nodeCacheListener = () -> {
System.out.println("receive Node Changed");
System.out.println(nodeCache.getCurrentData().getPath()+"---"+new String(nodeCache.getCurrentData().getData()));
};
nodeCache.getListenable().addListener(nodeCacheListener);
nodeCache.start();
}
//实现服务注册中心的时候,可以针对服务做动态感知
private static void addListenerWithChild(CuratorFramework curatorFramework) throws Exception {
PathChildrenCache nodeCache=new PathChildrenCache(curatorFramework,"/watch",true);
PathChildrenCacheListener nodeCacheListener= (curatorFramework1, pathChildrenCacheEvent) -> {
System.out.println(pathChildrenCacheEvent.getType() + "->" + new String(pathChildrenCacheEvent.getData().getData()));
} ;
nodeCache.getListenable().addListener(nodeCacheListener);
nodeCache.start(PathChildrenCache.StartMode.NORMAL);
}
}
//addListenerWithNode 测试命令
# create /watch watch-create
# set /watch watch-update
# delete /watch
//addListenerWithChild 测试命令
# create /watch/child child-create
# update /watch/child child-update
# delete /watch/child