Go:使用 consul 實現領導者選舉

在分佈式計算中,leader 選舉是指通過選舉產生一個領導者節點,該領導者節點能將任務分佈到其他不同節點。起初,集羣各節點是不感知哪個是”leader“節點的。在領導者選舉算法運行後,集羣中的每個節點識別一個特定的、唯一的節點作爲 leader,其他節點作爲 follower 節點。

Leader:作爲集羣特殊節點執行一些特殊操作。這些操作包括分配任務,修改一些數據的能力,甚至負責所有系統請求。
Follower:集羣中執行分配任務節點。
例如:Kubernetes 集羣

因爲我們在 Metro【一個開源項目】中使用 Consul 作爲數據庫註冊中心,所以我們決定在 leader 選舉實現中使用相同的方法。讓我們看看如何使用 Consul 實現分佈式鎖。

實現分佈式鎖

提供分佈式鎖需要的基本要求:
1、互斥:在任何時候只有一個節點持有鎖。
2、無死鎖:最終,即使鎖定資源的節點發生故障,其他節點也可以獲得鎖。
3、容錯:只要大多數節點還正常工作,鎖的獲取和釋放就可以完成。

consul

consul 是一個分佈式高可用的 Key-value 存儲系統(類似 etcd 和 zookeeper)。數據在 consul 中以 KV 格式存儲。

讓我們來看看 Consul 是如何滿足分佈式鎖定需求的。

互斥性

無死鎖

容錯

基於 consul 使用 Go 來實現 Leader 選舉

選舉可以通過以下 4 個步驟來實現:

1、創建具有存活時間 TTL 的 session

sessionID, _, err := client.Session().Create(&api.SessionEntry{
    Name: "my-service-lock",
    Behavior: "delete",
    TTL: "30s",
    LockDelay : 2 * time.Second
}, nil)

if err != nil {
    panic(err)
}
client.Session().RenewPeriodic(
        "30s",
        sessionID,
        nil,
        doneChan,
    )

這是一個阻塞調用,因此應該在單獨的 goroutine 中運行。通常,doneCh 是當前上下文的 Done 通道。

根據 Key 獲取鎖

isAcquired, _, err := client.KV().Acquire(&api.KVPair{
  Key:    "path/to/leader/key",
  Value:   []byte("any value"),
  Session: sessionID,
}, nil)

if err != nil {
  // 錯誤處理
}
// 如果沒有錯誤,isAquired爲true就是領導者節點

能夠獲得鎖的節點可以定爲 leader,其他節點成爲 follower。

觀察領導者 key

params := map[string]interface{}{}
params["type"] = "key"
params["key"] = "path/to/leader/key"

plan, err := watch.Parse(params)
if err != nil {
  // 錯誤處理
}

// 當所監視的key發生任何更改時調用該handler函數
plan.Handler = handler

//  阻塞調用,因此這段代碼應該在goroutine中運行
plan.RunWithClientAndHclog(client, nil)

handler 函數

func handler(index uint64, result interface{}) {
  log.Printf("watch data: %s", result)
  // 檢查返回的鍵是否有sessionID
  // 如果session ID存在,則key被某個節點獲取
  // 如果沒有,目前沒有領導者,嘗試獲取key
}

使用 watch 功能,可以觀察 key 的變化。如果領導者 key 發送變化,Consul 會通知的,因此 session 會被刪除 / 自動過期,領導者 key 釋放被其他節點獲取,導致 KV 的更新。所有 watch 這個 key 的節點都會被通知有變化。在收到通知後,所有節點可以檢查 key 的狀態,並運行新的選舉。

從概念上講很簡單,但在編寫生產系統時,需要考慮許多邊界情況,例如:

代碼

你可以參考 Metro 的領導者選舉實現【https://github.com/razorpay/metro/blob/5eb8881adbf5da6d387d1f4659916c83028dfb06/pkg/leaderelection/candidate.go#L56】。

參考文獻

  1. https://learn.hashicorp.com/tutorials/consul/application-leader-elections

  2. https://clivern.com/leader-election-with-consul-and-golang/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/bMSW2nRhM7Gyn9WFAw2ejg