Go語言使用Etcd實現(xiàn)分布式鎖
1 分布式鎖概述
談到分布式鎖,必然是因為單機鎖無法滿足要求,在現(xiàn)階段微服務(wù)多實例部署的情況下,單機語言級別的鎖,無法滿足并發(fā)互斥資源的安全訪問。常見的單機鎖如Java的jvm鎖Lock、synchronized,golang的Mutex等 對于分布式鎖有很多種實現(xiàn)方式,常見的有以下幾種:
- 基于數(shù)據(jù)庫:通過數(shù)據(jù)庫事務(wù)鎖例如
for update操作 - 基于緩存中間件:redis分布式鎖、etcd分布式鎖等
- 基于ZK臨時節(jié)點:zookeeper 臨時節(jié)點實現(xiàn)分布式鎖
每種方式實現(xiàn)的分布式鎖各有優(yōu)缺點簡單介紹一下:
- 數(shù)據(jù)庫實現(xiàn)不用額外引入新的中間件,減少系統(tǒng)的依賴性和不穩(wěn)定性,但性能不會太高,且并發(fā)量大時,對數(shù)據(jù)庫壓力比較大。
- ZK實現(xiàn)分布式,因為zk滿足了CP,能夠保證其數(shù)據(jù)一致性,不會出現(xiàn)加鎖成功后又丟失的問題,但相反性能會降低,并且可用性降低 CAP
A不是滿足的,詳細可以自行了解zk細節(jié) - redis 實現(xiàn):最大的優(yōu)點性能高,能保證AP,保證其高可用。但無法保證一致性,因為redis滿足的是AP,可能存在某一個時間節(jié)點集群數(shù)據(jù)S-M同步不一致。
2 分布式鎖要點
實現(xiàn)分布式鎖需要滿足一下幾點:
- 鎖載體:redis 受用 K-V 鍵值作為鎖載體,ZK使用臨時節(jié)點作為載體
- 鎖租期:進程持有分布式鎖后不能一直占用,如果因為宕機情況造成鎖釋放失敗,就會一直占用,reds 可以設(shè)置過期時間,zk臨時節(jié)點也會自動刪除。
- 其他要求:比如減少驚群效應(yīng)、可重入機制、公平鎖機制,不同的實現(xiàn)方式有的不能完全滿足。
分布式鎖選擇:
- qps不大的情況下,那種方式都可以
- 結(jié)合目前技術(shù)體系,在不引入新的技術(shù)中間件情況下解決問題
- qps并發(fā)極高,但容忍極少的數(shù)據(jù)丟失或者不一致,建議使用redis實現(xiàn)分布式鎖
- 如果業(yè)務(wù)要求任何情況下都不允許數(shù)據(jù)丟失,可以使用zk或者etcd實現(xiàn)
3 Etcd 實現(xiàn)機制
- 鎖載體: 使用 k-v 結(jié)構(gòu)實現(xiàn)
- 鎖租期: Etcd 通過
lease可以對 kv 設(shè)置租約,當(dāng)租約到期,kv 將失效刪除;避免長時間占用鎖不釋放放。 - 自動續(xù)期: Etcd 可以對租約進行自動續(xù)期,通過
KeepAlive實現(xiàn) - 公平鎖: 多個程序同時搶鎖時,會根據(jù)
Revision值大小依次獲得鎖,可以有效避免 “驚群效應(yīng)”,公平獲取。 - Watch 機制: 監(jiān)聽機制,Watch 機制支持 Watch 某個固定的 key或者目錄, key 或目錄發(fā)生變化,客戶端可以收到通知。
4 代碼實現(xiàn)
操作步驟:
- 初始化客戶端
- 創(chuàng)建一個session并設(shè)置默認租期30s
- 獲取指定前綴的鎖對象
- 加鎖
- 執(zhí)行業(yè)務(wù)
- 釋放鎖
代碼:
package main
import (
"context"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
"log"
"time"
)
func main() {
// 初始化客戶端
log.Println("客戶端初始化")
client, err := clientv3.New(clientv3.Config{Endpoints: []string{"localhost:2379"}, DialTimeout: time.Second * 3})
if err != nil {
log.Fatalf("客戶端初始化失敗:%v\n", err)
}
// 創(chuàng)建一個session并設(shè)置默認租期30s,即鎖默認超過30s會自動釋放(內(nèi)部會自動續(xù)期Etcd KeepAlive)
log.Println("Session初始化")
session, err := concurrency.NewSession(client, concurrency.WithTTL(30))
if err != nil {
log.Fatalf("Session初始化失敗:%v\n", err)
return
}
defer func(session *concurrency.Session) {
err := session.Close()
if err != nil {
log.Fatalf("Session關(guān)閉失敗:%v\n", err)
}
}(session)
// 獲取指定前綴的鎖對象
mutex := concurrency.NewMutex(session, "my-lock")
// 加鎖默認等待3s
log.Println("TryLock加鎖失敗不會等待")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
err = mutex.TryLock(ctx)
if err != nil {
log.Fatalf("加鎖失敗立即返回:%v\n", err)
return
}
//log.Println("加鎖最多等待3s")
//ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
//defer cancel()
//err = mutex.Lock(ctx)
//if err != nil {
// log.Fatalf("加鎖失敗:%v\n", err)
// return
//}
// Exe biz
log.Println("加鎖成功開始執(zhí)行業(yè)務(wù)")
for i := 1; i <= 10; i++ {
time.Sleep(time.Second)
log.Printf("執(zhí)行 %%%d ...", i*10)
}
// 釋放鎖
err = mutex.Unlock(context.TODO())
if err != nil {
log.Fatalf("釋放鎖失敗:%v\n", err)
return
}
log.Println("釋放鎖完成")
}測試結(jié)果


到此這篇關(guān)于Go語言使用Etcd實現(xiàn)分布式鎖的文章就介紹到這了,更多相關(guān)Go Etcd分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言映射內(nèi)部實現(xiàn)及基礎(chǔ)功能實戰(zhàn)
這篇文章主要為大家介紹了Go語言映射的內(nèi)部實現(xiàn)和基礎(chǔ)功能實戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>2022-03-03
掌握Golang中的select語句實現(xiàn)并發(fā)編程
Golang中的select語句用于在多個通道間選擇可讀或可寫的操作,并阻塞等待其中一個通道進行操作??梢杂糜趯崿F(xiàn)超時控制、取消和中斷操作等。同時,select語句支持default分支,用于在沒有任何通道可操作時執(zhí)行默認操作2023-04-04
go語言實現(xiàn)將重要數(shù)據(jù)寫入圖片中
本文給大家分享的是go語言實現(xiàn)將數(shù)據(jù)的二進制形式寫入圖像紅色通道數(shù)據(jù)二進制的低位,從而實現(xiàn)將重要數(shù)據(jù)隱藏,有需要的小伙伴參考下吧。2015-03-03

