Ultra fine! A case of Zookeeper election

Posted by w.geoghegan on Wed, 22 Dec 2021 12:38:31 +0100

Author: Regan Yue

Source: Hang Seng LIGHT cloud community

Ultra fine! A case of Zookeeper election

Today, let's take you to the case of realizing election with Zookeeper to help you better learn Zookeeper.

6, Determine whether to connect Zookeeper

func (electionManager *ElectionManager) isConnected() bool {
    if electionManager.ZKClientConn == nil {
        return false
    } else if electionManager.ZKClientConn.State() != zk.StateConnected {
        return false
    }
    return true
}

When initializing the Zookeeper connection, we need to judge whether Zookeeper is connected. In fact, we can judge whether there is a connection by whether the connection is nil. If we want to judge whether there is a problem with the connection, we'd better use the connected State() method. If it is ZK Stateconnected indicates that the connection is successful. If it is other, it indicates that the connection is abnormal. The values of connection exception and success are listed below.

StateUnknown           State = -1
StateDisconnected      State = 0
StateConnecting        State = 1
StateAuthFailed        State = 4
StateConnectedReadOnly State = 5
StateSaslAuthenticated State = 6
StateExpired           State = -112

StateConnected  = State(100)
StateHasSession = State(101)

As you can see, ZK Stateconnected is 100, so ZK Stateconnected is replaced by 100. I don't know whether it is feasible. You can try ~ StateUnknown, that is, - 1 means the state is unknown, StateDisconnected, that is, 0 means the state is not connected. I won't elaborate on others.

7, Logic of election

func (electionManager *ElectionManager) Run() {
    err := electionManager.electMaster()
    if err != nil {
        fmt.Println(err)
    }
    electionManager.watchMaster()
}

Isn't it familiar? This is what opens up the co process operation in the main function - election.

It is very simple. Directly select the master and then listen to the master node.

8, Election logic

func (electionManager *ElectionManager) electMaster() error {
    err := electionManager.initConnection()
    if err != nil {
        return err
    }
    isExist, _, err := electionManager.ZKClientConn.Exists(electionManager.ZKConfig.RootPath)
    if err != nil {
        return err
    }
    if !isExist {
        path, err := electionManager.ZKClientConn.Create(electionManager.ZKConfig.RootPath,
            nil, 0, zk.WorldACL(zk.PermAll))
        if err != nil {
            return err
        }
        if electionManager.ZKConfig.RootPath != path {
            return errors.New("Created" + electionManager.ZKConfig.RootPath + " !=" + path)
        }
    }

    masterPath := electionManager.ZKConfig.RootPath + electionManager.ZKConfig.MasterPath
    
    path, err := electionManager.ZKClientConn.Create(masterPath, nil, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
    if err == nil {
        if path == masterPath {
            fmt.Println("election master success")
            electionManager.IsMaster <- true
        } else {
            return errors.New("Created" + masterPath + "!=" + path)
        }
    } else {
        //Failed to create master node
        fmt.Println("election master Failed!", err)
        electionManager.IsMaster <- false
    }
    return nil
}

To start the election, you must first connect to Zookeeper. We write initConnection() to connect zk and judge whether there is a problem with the connection through the return value.

Then judge whether there is a root directory in zk. If not, create a root directory. Generally, the root directory data is not set first, and the flags are 0. To create a persistent node, the permissions are not controlled.

Let's introduce the Create() function~

Create() has four parameters. The first parameter is the path to be created, the second parameter is the data content in the node, the third parameter is the node type parameter, flag=0 indicates that this is a persistent node, and the fourth parameter is permission. Have the following permissions.

PermRead = 1 << iota
PermWrite
PermCreate
PermDelete
PermAdmin
PermAll = 0x1f

Use ZK Worldacl (zk. Permall) indicates that the node has no permission restrictions

Then splice the master address to create a master node. Because a temporary node needs to be created, the third parameter in Create() uses ZK Flagephemeral means to create a temporary node. Successful creation indicates that the election of the master is successful. Which client node has created the master is considered to be the election of which node as the master node.

After successful creation, write true to the connected isMaster. If unsuccessful, write false.

9, Listen to Master node

func (electionManager *ElectionManager) watchMaster() error {
    for {
    
        children, state, childCh, err := electionManager.ZKClientConn.ChildrenW(electionManager.ZKConfig.RootPath + electionManager.ZKConfig.MasterPath)
        if err != nil {
            fmt.Println("Monitoring failed!", err)
        }
        fmt.Println("Listen to child nodes", children, state)
        select {
        case childEvent := <-childCh:
            if childEvent.Type == zk.EventNodeDeleted {
                fmt.Println("Received znode Delete event for", childEvent)
                fmt.Println("Start Electing new master...")
                err = electionManager.electMaster()
                if err != nil {
                    fmt.Println("Elect a new master fail", err)
                }
            }
        }

    }
}

You need to listen to the child nodes under the root node of zookeeper. If the connection is disconnected or the corresponding child znode is deleted, re-election will be triggered. Therefore, you need to listen to all child nodes under the directory.

ChildrenW() will return the listening event. If the child node is deleted, that is, the event type is ZK After eventnodedeleted, the electMaster() is called again for election.

Topics: Go Zookeeper