用Etcd實現分散式鎖和選主
阿新 • • 發佈:2019-02-17
Etcd的v3版本官方client裡有一個concurrency的包,裡面實現了分散式鎖和選主。本文分析一下它是如何實現的。
在code中註釋介紹了具體的實現。
//m.pfx是字首,比如"service/lock/"
//s.Lease()是一個64位的整數值,etcd v3引入了lease(租約)的概念,concurrency包基於lease封裝了session,每一個客戶端都有自己的lease,也就是說每個客戶端都有一個唯一的64位整形值
//m.myKey類似於"service/lock/12345"
m.myKey = fmt.Sprintf("%s%x", m.pfx, s.Lease())
//etcdv3新引入的多鍵條件事務,替代了v2中Compare-And -put操作。etcdv3的多鍵條件事務的語意是先做一個比較(compare)操作,如果比較成立則執行一系列操作,如果比較不成立則執行另外一系列操作。有類似於C語言中的條件表示式。
//接下來的這部分實現瞭如果不存在這個key,則將這個key寫入到etcd,如果存在則讀取這個key的值這樣的功能。
//下面這一句,是構建了一個compare的條件,比較的是key的createRevision,如果revision是0,則存入一個key,如果revision不為0,則讀取這個key。
//revision是etcd一個全域性的序列號,每一個對etcd儲存進行改動都會分配一個這個序號,在v2中叫index,createRevision是表示這個key建立時被分配的這個序號。當key不存在時,createRivision是0 。
cmp := v3.Compare(v3.CreateRevision(m.myKey), "=", 0)
put := v3.OpPut(m.myKey, "", v3.WithLease(s.Lease()))
get := v3.OpGet(m.myKey)
resp, err := client.Txn(ctx).If(cmp).Then(put).Else(get).Commit()
if err != nil {
return err
}
m.myRev = resp.Header.Revision
if !resp.Succeeded {
m.myRev = resp.Responses [0].GetResponseRange().Kvs[0].CreateRevision
}
//如果上面的code操作成功了,則myRev是當前客戶端建立的key的revision值。
//waitDeletes等待匹配m.pfx ("service/lock/")這個字首(可類比在這個目錄下的)並且createRivision小於m.myRev-1所有key被刪除
//如果沒有比當前客戶端建立的key的revision小的key,則當前客戶端者獲得鎖
//如果有比它小的key則等待,比它小的被刪除
err = waitDeletes(ctx, client, m.pfx, m.myRev-1)
總結一下,上面的鎖的實現,所有的客戶端都在service/lock下建立一個自己的key,createrevision最小的那個客戶端獲得鎖,也就是最早建立key的客戶端獲得鎖,之後按照建立的時間先後依次獲得鎖。