CentOS環境安裝zookeeper服務並用golang實現分佈式系統的Leader選舉

1、準備工做node

1.下載安裝vmware,步驟省略。linux

2.下載CentOS系統ios包:http://isoredirect.centos.org/centos/7/isos/x86_64/CentOS-7-x86_64-Everything-1611.isoios

3.下載安裝Xshell5,步驟省略。git

4.下載安裝git,步驟省略。github

5.zookeeper官網:http://zookeeper.apache.org/golang

6.zookeeper用於golang的api:https://github.com/samuel/go-zookeepershell

7.vmware中依次點擊「建立新的虛擬機」->「典型」->「安裝程序光盤映像文件」選擇上面下載的ios文件,而後一路下一步便可快速安裝CentOS系統。apache

至於爲何安裝CentOS,其實其餘linux版本也能夠。安裝完成的CentOS系統以下圖:windows

 

2、安裝配置zookeepercentos

點擊屏幕左上角的Applications,打開Terminal,下面咱們把zookeeper下載到/usr/local目錄下,這個目錄能夠理解爲windows下的C:/Progrem Files/目錄。

首先切換到root權限:

輸入su,回車,看到Password:後輸入密碼,注意密碼不會顯示出來,輸入完畢直接回車就好。

接下來執行命令進入/usr/local目錄:

cd /usr/local

下載 zookeeper-3.5.3-beta.tar.gz:

wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.5.3-beta/zookeeper-3.5.3-beta.tar.gz

terminal裏會出現下載進度條,進度條讀完後文件下載完畢,咱們先將其解壓縮:

tar -xvf zookeeper-3.5.3-beta.tar.gz

 

接下來咱們進入conf目錄進行設置:

cd zookeeper-3.5.3-beta/conf/

複製 zoo_sample.cfg 文件的並命名爲爲 zoo.cfg:

cp zoo_sample.cfg zoo.cfg

咱們編輯zoo.cfg,進行相關設置:

vi zoo.cfg

這個時候若是想修改這個文件的內容,按下間鍵盤上的「i」鍵,最下方就會變成INSERT,就能夠修改了

移動光標,將

dataDir=/tmp/zookeeper

改成

dataDir=/usr/local/zookeeper-3.5.3-beta

 

按Esc退出編輯模式,而後輸入:wq回車進行保存並關閉文件。

 

下面咱們查看一下虛擬機的局域網ip地址:

ifconfig

 

能夠看到我虛擬機的IP地址爲:192.168.40.129

這裏咱們要用到一個軟件xshell5,其實在CentOS的terminal下運行也是同樣的,可是若是zookeeper安裝在服務器端而不是本地虛擬機的話,使用xshell5就會顯得更方便了,因此這裏咱們使用xshell5。

打開xshell5,登陸咱們的虛擬機:

 

用戶名填root,密碼是虛擬機的密碼。

進入zookeeper的運行目錄:

cd /usr/local/zookeeper-3.5.3-beta/bin

下面咱們啓動zookeeper的服務:

./zkServer.sh start

 

出現STARTED,說明咱們的zookeeper已經啓動了,咱們能夠經過它提供的clint端在命令行下直接進行一些簡易的操做,輸入:

./zkCli.sh

能夠看到咱們已經進入了zookeeper的clint端。

 

輸入help,能夠查看一些命令行下的命令。

 

3、golang實現分佈式系統的Leader選舉

下面咱們利用搭建好的zookeeper,使用golang實現分佈式系統的Leader選舉

安裝配置golang環境不是本文討論的重點,這裏省略。

咱們能夠利用git來獲取,git可自行下載安裝,下面是git命令。

查看golang的位置:

which go

設置GOROOT:

export GOROOT=/c/Go

我將golang程序放到了d盤的golang文件夾下,那麼設置一下GOPATH:

export GOPATH=/d/golang/

獲取zookeeper對go的api:

go get github.com/samuel/go-zookeeper/zk

下面在d盤golang文件夾下新建一個go項目,寫入以下代碼來模擬Leader選舉的過程:

package main

import (
	"github.com/samuel/go-zookeeper/zk"
	"errors"
	"time"
	"fmt"
)

type ZookeeperConfig struct {
	Servers    []string
	RootPath   string
	MasterPath string
}

type ElectionManager struct {
	ZKClientConn *zk.Conn
	ZKConfig     *ZookeeperConfig
	IsMasterQ    chan bool
}

func NewElectionManager(zkConfig *ZookeeperConfig, isMasterQ chan bool) *ElectionManager {
	electionManager := &ElectionManager{
		nil,
		zkConfig,
		isMasterQ,
	}
	electionManager.initConnection()
	return electionManager
}

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

// 判斷是否成功鏈接到zookeeper
func (electionManager *ElectionManager) isConnected() bool {
	if electionManager.ZKClientConn == nil {
		return false
	} else if electionManager.ZKClientConn.State() != zk.StateConnected {
		return false
	}
	return true
}

// 初始化zookeeper鏈接
func (electionManager *ElectionManager) initConnection() error {
	// 鏈接爲空,或鏈接不成功,獲取zookeeper服務器的鏈接
	if !electionManager.isConnected() {
		conn, connChan, err := zk.Connect(electionManager.ZKConfig.Servers, time.Second)
		if err != nil {
			return err
		}
		// 等待鏈接成功
		for {
			isConnected := false
			select {
			case connEvent := <-connChan:
				if connEvent.State == zk.StateConnected {
					isConnected = true
					fmt.Println("connect to zookeeper server success!")
				}
			case _ = <-time.After(time.Second * 3): // 3秒仍未鏈接成功則返回鏈接超時
				return errors.New("connect to zookeeper server timeout!")
			}
			if isConnected {
				break
			}
		}
		electionManager.ZKClientConn = conn
	}
	return nil
}

// 選舉master
func (electionManager *ElectionManager) electMaster() error {
	err := electionManager.initConnection()
	if err != nil {
		return err
	}
	// 判斷zookeeper中是否存在root目錄,不存在則建立該目錄
	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("Create returned different path " + electionManager.ZKConfig.RootPath + " != " + path)
		}
	}

	// 建立用於選舉master的ZNode,該節點爲Ephemeral類型,表示客戶端鏈接斷開後,其建立的節點也會被銷燬
	masterPath := electionManager.ZKConfig.RootPath + electionManager.ZKConfig.MasterPath
	path, err := electionManager.ZKClientConn.Create(masterPath, nil, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
	if err == nil { // 建立成功表示選舉master成功
		if path == masterPath {
			fmt.Println("elect master success!")
			electionManager.IsMasterQ <- true
		} else {
			return errors.New("Create returned different path " + masterPath + " != " + path)
		}
	} else { // 建立失敗表示選舉master失敗
		fmt.Printf("elect master failure, ", err)
		electionManager.IsMasterQ <- false
	}
	return nil
}

// 監聽zookeeper中master znode,若被刪除,表示master故障或網絡遲緩,從新選舉
func (electionManager *ElectionManager) watchMaster() {
	for {
		// watch zk根znode下面的子znode,當有鏈接斷開時,對應znode被刪除,觸發事件後從新選舉
		children, state, childCh, err := electionManager.ZKClientConn.ChildrenW(electionManager.ZKConfig.RootPath + electionManager.ZKConfig.MasterPath)
		if err != nil {
			fmt.Println("watch children error, ", err)
		}
		fmt.Println("watch children result, ", children, state)
		select {
		case childEvent := <-childCh:
			if childEvent.Type == zk.EventNodeDeleted {
				fmt.Println("receive znode delete event, ", childEvent)
				// 從新選舉
				fmt.Println("start elect new master ...")
				err = electionManager.electMaster()
				if err != nil {
					fmt.Println("elect new master error, ", err)
				}
			}
		}
	}
}
func main() {
	// zookeeper配置
	zkConfig := &ZookeeperConfig{
		Servers:    []string{"192.168.40.129"},
		RootPath:   "/ElectMasterDemo",
		MasterPath: "/master",
	}
	// main goroutine 和 選舉goroutine之間通訊的channel,同於返回選角結果
	isMasterChan := make(chan bool)

	var isMaster bool

	// 選舉
	electionManager := NewElectionManager(zkConfig, isMasterChan)
	go electionManager.Run()

	for {
		select {
		case isMaster = <-isMasterChan:
			if isMaster {
				// do some job on master
				fmt.Println("do some job on master")
			}
		}
	}
}

 

其中,main函數的ip改寫成上面查到的虛擬機ip。

在Xshell5中輸入:

ls /

咱們能夠看到目前根目錄下只有一個zookeeper。

要使用golang程序成功調用zookeeper,還須要關掉咱們CentOS系統下的防火牆。

在Xshell5中輸入:

systemctl | grep fire

能夠看到防火牆正在運行。

咱們先中止防火牆:

stop firewalld

而後禁用防火牆:

disable firewalld

保存修改:

iptables-save

 

成功關掉防火牆以後,運行上述go程序,咱們能夠看到第一個進程成功成爲了leader:

GOROOT=C:\Go
GOPATH=D:/golang
C:\Go\bin\go.exe build -i -o C:\Users\renjiashuo\AppData\Local\Temp\Build_main_go_and_rungo D:/golang/src/leader/main.go
"C:\Program Files\JetBrains\IntelliJ IDEA 2017.1.5\bin\runnerw.exe" C:\Users\renjiashuo\AppData\Local\Temp\Build_main_go_and_rungo
connect to zookeeper server success!
2017/07/27 21:02:38 Connected to 192.168.40.129:2181
2017/07/27 21:02:38 Authenticated: id=72057981844586499, timeout=4000
2017/07/27 21:02:38 Re-submitting `0` credentials after reconnect
elect master success!
do some job on master
watch children result,  [] &{9 9 1501160558009 1501160558009 0 0 0 72057981844586499 0 0 9}

  

不要關閉這個程序,再執行一個此go程序,咱們能夠看到,由於有第一個進程已經成爲了leader,第二個進程只能等待:

GOROOT=C:\Go
GOPATH=D:/golang
C:\Go\bin\go.exe build -i -o C:\Users\renjiashuo\AppData\Local\Temp\Build_main_go_and_run1go D:/golang/src/leader/main.go
"C:\Program Files\JetBrains\IntelliJ IDEA 2017.1.5\bin\runnerw.exe" C:\Users\renjiashuo\AppData\Local\Temp\Build_main_go_and_run1go
connect to zookeeper server success!
2017/07/27 21:05:29 Connected to 192.168.40.129:2181
2017/07/27 21:05:29 Authenticated: id=72057981844586500, timeout=4000
2017/07/27 21:05:29 Re-submitting `0` credentials after reconnect
elect master failure, %!(EXTRA *errors.errorString=zk: node already exists)watch children result,  [] &{9 9 1501160558009 1501160558009 0 0 0 72057981844586499 0 0 9}

  

此時咱們關閉第一個進程,能夠看到第二個進程的變化:

GOROOT=C:\Go
GOPATH=D:/golang
C:\Go\bin\go.exe build -i -o C:\Users\renjiashuo\AppData\Local\Temp\Build_main_go_and_run1go D:/golang/src/leader/main.go
"C:\Program Files\JetBrains\IntelliJ IDEA 2017.1.5\bin\runnerw.exe" C:\Users\renjiashuo\AppData\Local\Temp\Build_main_go_and_run1go
connect to zookeeper server success!
2017/07/27 21:05:29 Connected to 192.168.40.129:2181
2017/07/27 21:05:29 Authenticated: id=72057981844586500, timeout=4000
2017/07/27 21:05:29 Re-submitting `0` credentials after reconnect
elect master failure, %!(EXTRA *errors.errorString=zk: node already exists)watch children result,  [] &{9 9 1501160558009 1501160558009 0 0 0 72057981844586499 0 0 9}
receive znode delete event,  {EventNodeDeleted Unknown /ElectMasterDemo/master <nil> }
start elect new master ...
connect to zookeeper server success!
2017/07/27 21:06:28 Connected to 192.168.40.129:2181
2017/07/27 21:06:28 Authenticated: id=72057981844586501, timeout=4000
2017/07/27 21:06:28 Re-submitting `0` credentials after reconnect
elect master success!
do some job on master
watch children result,  [] &{14 14 1501160787685 1501160787685 0 0 0 72057981844586501 0 0 14}

  

第二個進程成功成爲了leader。

程序代碼的註釋比較詳細,這裏不作詳解,全部調用的api接口均可以進入查看其go源碼。

自此,CentOS環境安裝zookeeper服務並用golang實現分佈式系統的Leader選舉實現完畢。

相關文章
相關標籤/搜索