Dubbo source code analysis ten: Detailed explanation of ZooKeeper and client framework Curator

Insert picture description here

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

Insert picture description here
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
Insert picture description here
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
Insert picture description here
. 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

  1. 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)
  2. 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

Guess you like

Origin blog.csdn.net/zzti_erlie/article/details/109409326