Zookeeper learning series: 3. Use zookeeper to do elections and locks

Zookeeper learning series: 3. Use zookeeper to do elections and locks

Before, I only understood that zk can do naming and configuration services. Now learn how to use it as election and lock, and furthermore, you can build a distributed system in the master-slave mode.

Why is it called Zoo? "Because the distributed system to be coordinated is a zoo."

ZooKeeper is a neutral service, used to manage configuration information, naming, provide distributed synchronization, and can also combine services. All these types of Services will be used in distributed applications. Each time writing these services will involve a lot of bug fixes and competition. It is precisely because of the difficulty of writing these services that they are usually ignored, which makes it difficult to manage the application when there are changes in the application. Even if handled properly, the different methods of implementing these services can make deploying applications difficult to manage.

The code below is the java version of the reference, through the service to coordinate each independent PHP script, and let them agree to become a Leader (so called Leader election). When the leader exits (or crashes), the worker can detect and elect a new leader. In this way, you can understand how the general master-slave structured distributed system realizes how to schedule, and zk is a good thing.

1. you need to understand the mode of creating nodes:

PERSISTENT: Persistent directory node, the data stored in this directory node will not be lost;

PERSISTENT_SEQUENTIAL: A directory node that is automatically numbered in sequence. This directory node will automatically increase by 1 according to the number of nodes that currently exist, and then return to the client the name of the directory node that has been successfully created;

EPHEMERAL: Temporary directory node. Once the client and server ports of this node are created, that is, the session times out, this node will be automatically deleted;

EPHEMERAL_SEQUENTIAL: Temporary automatic numbering of nodes.

Temporary nodes play a very important role in leader election and lock services. 

1. Election

Program logic:

1) First create the root node/cluster, and create its own child nodes, using/cluster/w- as the prefix, use the temporary automatic numbering node mode to create nodes

2) Get all the child nodes of/cluster and sort them. When it is found that it is the first node, it will self-elect as leader, otherwise it will be regarded as follower

3) Register for monitoring events, when the previous node in/cluster changes, return to 2)

In this way, automatic election is realized. When a node is unavailable after the timeout period, a new leader is automatically generated, and an early warning can also be given based on the current number of nodes.

package zookeeper;

import org.apache.zookeeper.*;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 *
 * @author guanpu
 * Date: 14-10-22
 * Time: 5:11 PM
 * To change this template use File | Settings | File Templates.
 */
public class Worker extends ZooKeeper implements Runnable, Watcher {
    public static final String NODE_NAME = "/cluster";
    public String znode;
    private boolean leader;

    public Worker(String connectString, int sessionTimeout, Watcher watcher) throws IOException {
        super(connectString, sessionTimeout, watcher);
    }

    public boolean register() throws InterruptedException, KeeperException {
        if (this.exists(NODE_NAME, null) == null) {
            this.create(NODE_NAME, "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
                    CreateMode.PERSISTENT);
        }
        znode = this.create(NODE_NAME + "/w-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL_SEQUENTIAL);
        znode = znode.replace(NODE_NAME + "/", "");
        String node = watchPrevious();
        if (node.equals(znode)) {
            System.out.println("nobody here ,i am leader");
            leader = true;
        } else {
            System.out.println("i am watching");
        }
        return true;
    }

    private String watchPrevious() throws InterruptedException, KeeperException {
        List<String> works = this.getChildren(NODE_NAME, this);
        Collections.sort(works);
        System.out.println(works);
        int i = 0;
        for (String work: works) {
            if (znode.equals(work)) {
                if (i> 0) {
                  //this.getData(NODE_NAME + "/" + works.get(i-1), this, null);
                    return works.get(i-1);
                }
                return works.get(0);
            }
        }
        return "";

    }

    @Override
    public void run() {
        try {
            this.register();
        } catch (InterruptedException e) {
        } catch (KeeperException e) {
        }
        while (true) {
            try {
                if (leader) {
                    System.out.println("leading");
                } else {
                    System.out.println("following");
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }
    }

    public static void main(String[] args) {
        try {
            String hostPort = "10.16.73.22,10.16.73.12,10.16.73.13";
            new Thread(new Worker(hostPort, 3000, null)).start();
        } catch (IOException e) {
        }
    }


    @Override
    public void process(WatchedEvent event) {
        String t = String.format("hello event! type=%s, stat=%s, path=%s", event.getType(), event.getState(), event.getPath());
        System.out.println(t);
        System.out.println("hello ,my cluster id is :"+znode);
        String node = "";
        try {
            node = this.watchPrevious();
        } catch (InterruptedException e) {
        } catch (KeeperException e) {
        }

        if (node.equals(znode)) {
            System.out.println("process: nobody here ,i am leader");
            leader = true;
        } else {
            System.out.println("process: i am watching");
        }
    }
}

 Start at least three terminals to simulate the situation of Leader crash. Use Ctrl+c or other methods to exit the first script. There will be no changes in the beginning, and the worker can continue to work. Later, ZooKeeper will discover the timeout and elect a new leader.

There are two problems in porting php to java. The first is watcher registration. When the parent class initialization is not completed for the first time, you cannot call itself as a watcher, and it will report that the watcher calls a null pointer once.

second question:

 this.getData(NODE_NAME + "/" + works.get(i-1), this, null);

This does not take effect. The method comment is that when the node is changed and removed, the process of the watcher is triggered, but it is not triggered in the experiment. Is the automatic deletion of the system in Java not classified into these two operations?

The php version is normal, as a residual problem. For the normal operation of the program, change to List<String> works = this.getChildren(NODE_NAME, this); The process method is executed when the child node changes. But this will lead to a herd effect. It will be obvious when there are many cluster servers and the bandwidth delay is large. The leader's status change will cause the change of all followers. A short connection of one of the followers will also cause the entire cluster to respond to this change.

2. the lock 

Locked:

1) zk calls the create() method to create a node with the path format "_locknode_/lock-", the type is sequence and ephemeral, temporary nodes and sequence numbers

2) Call the getChildren() method on the created lock node to obtain the smallest number node in the lock directory, and do not set watch

3) If the node obtained in step 2 is the node created in step 1, then the client obtains the lock and then exits the operation

4) The client calls the exists() method on the lock directory, and sets watch to monitor the status of the continuous temporary nodes with a smaller serial number in the lock directory.

5) If the status of the monitoring node changes, skip to step 2 and continue subsequent operations until the lock competition is exited.

Unlock:

Just delete the temporary node created in step 1 of the locking operation.

references:

http://anykoro.sinaapp.com/2013/04/05/Use apache-zookeeper to deploy php applications/

Reference: https://cloud.tencent.com/developer/article/1067420 zookeeper learning series: 3. Use zookeeper for election and lock-Cloud + Community-Tencent Cloud