Introduction to Zookeeper
The data model of zookeeper is similar to the file system. Each node is called znode, which is the smallest data unit in zookeeper. Each znode can report data and mount child nodes to form a hierarchical attribute structure.
Zookeeper can create the following types of nodes
Node type | Explanation |
---|---|
Persistent node | Create a node as a persistent node, and the data will always be stored on the zookeeper server. Even if the session between the client and server that created the node is closed, the node will still not be deleted |
Persistent sequence node | On the basis of persistent nodes, the order of nodes is added |
Temporary node | Create the node as a temporary node, the data will not always be stored on the zookeeper server, when the client session that created the temporary node is closed, the node will be deleted on the corresponding zookeeper server |
Temporary Sequence Node | On the basis of temporary nodes, the order of nodes is added |
Briefly demonstrate the commonly used commands
create [-s] [-e] path data acl
-s: create sequential nodes
-e: create temporary nodes
path: path
data: data
acl: permissions
Create creates a persistent node by default
create /level-1 123
create /level-1/level-1-2 456
get /level-1(获取节点level-1的值,输出123)
ls /level-1 (获取节点level-1的子节点,输出[level-1-2])
// 创建一个顺序节点
create -s /nodes 123(输出nodes0000000003)
create -s /nodes 456(输出nodes0000000004)
After executing the above command, the data structure is as follows
Here is a brief talk about the characteristics of sequential nodes. Every time a sequential node is created, zk will automatically add a 10-digit number (counter) after the path, such as <path >0000000001, <path >0000000002, ... This counter can be guaranteed to be unique under the same parent node. A 4-byte signed integer is used inside zk to represent this counter, which means that when the size of the counter exceeds 2147483647, an overflow will occur. Each time a temporary node is created under the parent node, the size is increased by 1. 3 to 4 in the picture above
Use of Curator
Curator is a set of zookeeper client open sourced by netflix company. It can help us simplify the operation of zookeeper. Solved many underlying problems, such as reconnection, repeated registration of Watcher and NodeExistsException, etc.
In addition, Curator also provides various application scenarios of Zookeeper: Recipe, shared lock service, Master election mechanism and distributed counters, etc.
Curator is divided into the following modules. The
most commonly used ones are curator-framework (encapsulation of zookeeper api, such as adding connection management, retry mechanism, etc.) and curator-recipes (implementation of typical application scenarios of zookeeper), curator-client ( Package of zookeeper client)
When we use it, we only need to add the following dependencies
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
Demonstrate the basic use of Curator Api
@Slf4j
public class ApiDemo {
private CuratorFramework client;
/**
* RetryPolicy 是重试策略接口
* https://www.cnblogs.com/qingyunzong/p/8666288.html
*/
@Test
@Before
public void connect() {
String connectString = "myhost:2181";
// 重试3次,每次间隔1000ms
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client = CuratorFrameworkFactory.newClient(connectString, retryPolicy);
client.start();
}
/**
* 创建一个持久节点
*/
@Test
public void createPersistent() throws Exception {
// 创建一个内容为空的节点
client.create().forPath("/persistent");
// 创建包含内容的节点
client.create().forPath("/persistentContent", "我是内容".getBytes());
}
/**
* 创建临时节点
*/
@Test
public void createEphemeral() throws Exception {
// 创建一个内容为空的节点
client.create().withMode(CreateMode.EPHEMERAL).forPath("Ephemeral");
// 创建包含内容的节点
client.create().withMode(CreateMode.EPHEMERAL).forPath("/ephemeralContent", "我是内容".getBytes());
}
/**
* 获取值
*/
@Test
public void getData() throws Exception {
client.getData().forPath("/persistentContent");
}
/**
* 更新值
*/
@Test
public void setData() throws Exception {
client.setData().forPath("/persistentContent", "新内容".getBytes());
}
/**
* 删除
*/
@Test
public void delete() throws Exception {
client.delete().forPath("/persistent");
}
}
Detailed event
Zookeeper provides distributed data publishing/subscription, allowing the client to register a watcher to the server. When some specified events on the server trigger the watcher, then an event notification will be sent to the specified client to realize the distributed notification function .
Zookeeper defines the Watcher interface to represent a standard event handler, and contains
two enumeration classes KeeperState and EventType to represent the notification state and event type
The relationship between KeeperState and EventType is as follows
. Monitoring events on the server is an important task for the client to operate the server. In the api of curator, there are two modes of event monitoring
- The standard observation mode can only be monitored once (that is, the Watcher interface in Zookeeper, in order to throw an exception, curator redefines a CuratorWatcher interface)
- Cache monitor mode, can monitor multiple times
The cache monitoring mode can be understood as the comparison process between the local cache view and the remote zookeeper view. When the znode status change of the zk cluster is sensed, events will be triggered, and the registered listeners will process these events.
There are three types of cache monitoring
Monitor type | Explanation |
---|---|
Node Cache | Monitor ZNode node |
Path Cache | Monitor ZNode child nodes |
Tree Cache | Monitor ZNode node and its children |
Still go directly to the example
@Test
public void watcher() throws Exception {
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent event) {
// 只输出一次
// /watchDemo SyncConnected NodeDataChanged
System.out.println(event.getPath() + " " + event.getState() + " " + event.getType());
}
};
String path = "/watchDemo";
if (client.checkExists().forPath(path) == null) {
client.create().forPath(path);
}
client.getData().usingWatcher(watcher).forPath(path);
client.setData().forPath(path, "第一个变更的内容".getBytes());
client.setData().forPath(path, "第二个变更的内容".getBytes());
TimeUnit.SECONDS.sleep(3);
}
@Test
public void curatorWatcher() throws Exception {
CuratorWatcher watcher = new CuratorWatcher() {
@Override
public void process(WatchedEvent event) throws Exception {
// 只输出一次
// /watchDemo SyncConnected NodeDataChanged
System.out.println(event.getPath() + " " + event.getState() + " " + event.getType());
}
};
String path = "/watchDemo";
if (client.checkExists().forPath(path) == null) {
client.create().forPath(path);
}
client.getData().usingWatcher(watcher).forPath(path);
client.setData().forPath(path, "第一个变更的内容".getBytes());
client.setData().forPath(path, "第二个变更的内容".getBytes());
TimeUnit.SECONDS.sleep(3);
}
You can see that whether it is the Watcher interface in zookeeper or the CuratorWatcher interface in curator, the event can only be monitored once
@Test
public void treeCacheListener() throws Exception {
String bossPath = "/treeCache";
String workerPath = "/treeCache/id-";
if (client.checkExists().forPath(bossPath) == null) {
client.create().forPath(bossPath);
}
TreeCache treeCache = new TreeCache(client, bossPath);
TreeCacheListener listener = ((CuratorFramework client, TreeCacheEvent event) -> {
String path = null;
String content = null;
switch (event.getType()) {
case NODE_ADDED:
log.info("节点增加");
path = event.getData().getPath();
content = new String(event.getData().getData());
break;
case NODE_UPDATED:
log.info("节点更新");
path = event.getData().getPath();
content = new String(event.getData().getData());
break;
case NODE_REMOVED:
log.info("节点移除");
path = event.getData().getPath();
content = new String(event.getData().getData());
break;
default:
break;
}
// 事件类型为: NODE_ADDED, 路径为: /treeCache, 内容为: 192.168.97.69
// 事件类型为: INITIALIZED, 路径为: null, 内容为: null
// 事件类型为: NODE_ADDED, 路径为: /treeCache/id-0, 内容为: 0
// 事件类型为: NODE_ADDED, 路径为: /treeCache/id-1, 内容为: 1
// 事件类型为: NODE_REMOVED, 路径为: /treeCache/id-0, 内容为: 0
// 事件类型为: NODE_REMOVED, 路径为: /treeCache/id-1, 内容为: 1
// 事件类型为: NODE_REMOVED, 路径为: /treeCache, 内容为: 192.168.97.69
log.info("事件类型为: {}, 路径为: {}, 内容为: {}", event.getType(), path, content);
});
treeCache.getListenable().addListener(listener);
treeCache.start();
// 创建2个子节点
for (int i = 0; i < 2; i++) {
client.create().forPath(workerPath + i, String.valueOf(i).getBytes());
}
// 删除2个子节点
for (int i = 0; i < 2; i++) {
client.delete().forPath(workerPath + i);
}
// 删除当前节点
client.delete().forPath(bossPath);
TimeUnit.SECONDS.sleep(3);
}
As you can see, the cache monitoring mode can be registered repeatedly
The other content of zookeeper will not be introduced much. After understanding zookeeper and curator api, there is not much problem with the zookeeper related code in the Dubbo registry module.
Reference blog
[1] https://www.cnblogs.com/crazymakercircle/p/10228385.html