Go語(yǔ)言使用Etcd實(shí)現(xiàn)分布式鎖
1 分布式鎖概述
談到分布式鎖,必然是因?yàn)閱螜C(jī)鎖無(wú)法滿足要求,在現(xiàn)階段微服務(wù)多實(shí)例部署的情況下,單機(jī)語(yǔ)言級(jí)別的鎖,無(wú)法滿足并發(fā)互斥資源的安全訪問(wèn)。常見(jiàn)的單機(jī)鎖如Java的jvm鎖Lock
、synchronized
,golang的Mutex
等 對(duì)于分布式鎖有很多種實(shí)現(xiàn)方式,常見(jiàn)的有以下幾種:
- 基于數(shù)據(jù)庫(kù):通過(guò)數(shù)據(jù)庫(kù)事務(wù)鎖例如
for update
操作 - 基于緩存中間件:redis分布式鎖、etcd分布式鎖等
- 基于ZK臨時(shí)節(jié)點(diǎn):zookeeper 臨時(shí)節(jié)點(diǎn)實(shí)現(xiàn)分布式鎖
每種方式實(shí)現(xiàn)的分布式鎖各有優(yōu)缺點(diǎn)簡(jiǎn)單介紹一下:
- 數(shù)據(jù)庫(kù)實(shí)現(xiàn)不用額外引入新的中間件,減少系統(tǒng)的依賴性和不穩(wěn)定性,但性能不會(huì)太高,且并發(fā)量大時(shí),對(duì)數(shù)據(jù)庫(kù)壓力比較大。
- ZK實(shí)現(xiàn)分布式,因?yàn)閦k滿足了CP,能夠保證其數(shù)據(jù)一致性,不會(huì)出現(xiàn)加鎖成功后又丟失的問(wèn)題,但相反性能會(huì)降低,并且可用性降低 CAP
A
不是滿足的,詳細(xì)可以自行了解zk細(xì)節(jié) - redis 實(shí)現(xiàn):最大的優(yōu)點(diǎn)性能高,能保證AP,保證其高可用。但無(wú)法保證一致性,因?yàn)閞edis滿足的是AP,可能存在某一個(gè)時(shí)間節(jié)點(diǎn)集群數(shù)據(jù)S-M同步不一致。
2 分布式鎖要點(diǎn)
實(shí)現(xiàn)分布式鎖需要滿足一下幾點(diǎn):
- 鎖載體:redis 受用 K-V 鍵值作為鎖載體,ZK使用臨時(shí)節(jié)點(diǎn)作為載體
- 鎖租期:進(jìn)程持有分布式鎖后不能一直占用,如果因?yàn)殄礄C(jī)情況造成鎖釋放失敗,就會(huì)一直占用,reds 可以設(shè)置過(guò)期時(shí)間,zk臨時(shí)節(jié)點(diǎn)也會(huì)自動(dòng)刪除。
- 其他要求:比如減少驚群效應(yīng)、可重入機(jī)制、公平鎖機(jī)制,不同的實(shí)現(xiàn)方式有的不能完全滿足。
分布式鎖選擇:
- qps不大的情況下,那種方式都可以
- 結(jié)合目前技術(shù)體系,在不引入新的技術(shù)中間件情況下解決問(wèn)題
- qps并發(fā)極高,但容忍極少的數(shù)據(jù)丟失或者不一致,建議使用redis實(shí)現(xiàn)分布式鎖
- 如果業(yè)務(wù)要求任何情況下都不允許數(shù)據(jù)丟失,可以使用zk或者etcd實(shí)現(xiàn)
3 Etcd 實(shí)現(xiàn)機(jī)制
- 鎖載體: 使用 k-v 結(jié)構(gòu)實(shí)現(xiàn)
- 鎖租期: Etcd 通過(guò)
lease
可以對(duì) kv 設(shè)置租約,當(dāng)租約到期,kv 將失效刪除;避免長(zhǎng)時(shí)間占用鎖不釋放放。 - 自動(dòng)續(xù)期: Etcd 可以對(duì)租約進(jìn)行自動(dòng)續(xù)期,通過(guò)
KeepAlive
實(shí)現(xiàn) - 公平鎖: 多個(gè)程序同時(shí)搶鎖時(shí),會(huì)根據(jù)
Revision
值大小依次獲得鎖,可以有效避免 “驚群效應(yīng)”,公平獲取。 - Watch 機(jī)制: 監(jiān)聽(tīng)機(jī)制,Watch 機(jī)制支持 Watch 某個(gè)固定的 key或者目錄, key 或目錄發(fā)生變化,客戶端可以收到通知。
4 代碼實(shí)現(xiàn)
操作步驟:
- 初始化客戶端
- 創(chuàng)建一個(gè)session并設(shè)置默認(rèn)租期30s
- 獲取指定前綴的鎖對(duì)象
- 加鎖
- 執(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)建一個(gè)session并設(shè)置默認(rèn)租期30s,即鎖默認(rèn)超過(guò)30s會(huì)自動(dòng)釋放(內(nèi)部會(huì)自動(dòng)續(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) // 獲取指定前綴的鎖對(duì)象 mutex := concurrency.NewMutex(session, "my-lock") // 加鎖默認(rèn)等待3s log.Println("TryLock加鎖失敗不會(huì)等待") 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("加鎖成功開(kāi)始執(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("釋放鎖完成") }
測(cè)試結(jié)果
到此這篇關(guān)于Go語(yǔ)言使用Etcd實(shí)現(xiàn)分布式鎖的文章就介紹到這了,更多相關(guān)Go Etcd分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Go使用Redis實(shí)現(xiàn)分布式鎖的常見(jiàn)方法
- Golang使用Zookeeper實(shí)現(xiàn)分布式鎖
- Go分布式鏈路追蹤實(shí)戰(zhàn)探索
- 分布式架構(gòu)在Go語(yǔ)言網(wǎng)站的應(yīng)用
- Golang微服務(wù)框架Kratos實(shí)現(xiàn)分布式任務(wù)隊(duì)列Asynq的方法詳解
- 基于Golang實(shí)現(xiàn)Redis分布式鎖解決秒殺問(wèn)題
- 用Go語(yǔ)言編寫(xiě)一個(gè)簡(jiǎn)單的分布式系統(tǒng)
- SpringBoot分布式文件存儲(chǔ)數(shù)據(jù)庫(kù)mongod
- 在Go語(yǔ)言開(kāi)發(fā)中實(shí)現(xiàn)高性能的分布式日志收集的方法
相關(guān)文章
Go語(yǔ)言映射內(nèi)部實(shí)現(xiàn)及基礎(chǔ)功能實(shí)戰(zhàn)
這篇文章主要為大家介紹了Go語(yǔ)言映射的內(nèi)部實(shí)現(xiàn)和基礎(chǔ)功能實(shí)戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2022-03-03掌握Golang中的select語(yǔ)句實(shí)現(xiàn)并發(fā)編程
Golang中的select語(yǔ)句用于在多個(gè)通道間選擇可讀或可寫(xiě)的操作,并阻塞等待其中一個(gè)通道進(jìn)行操作。可以用于實(shí)現(xiàn)超時(shí)控制、取消和中斷操作等。同時(shí),select語(yǔ)句支持default分支,用于在沒(méi)有任何通道可操作時(shí)執(zhí)行默認(rèn)操作2023-04-04go語(yǔ)言實(shí)現(xiàn)將重要數(shù)據(jù)寫(xiě)入圖片中
本文給大家分享的是go語(yǔ)言實(shí)現(xiàn)將數(shù)據(jù)的二進(jìn)制形式寫(xiě)入圖像紅色通道數(shù)據(jù)二進(jìn)制的低位,從而實(shí)現(xiàn)將重要數(shù)據(jù)隱藏,有需要的小伙伴參考下吧。2015-03-03golang 內(nèi)存對(duì)齊的實(shí)現(xiàn)
在代碼編譯階段,編譯器會(huì)對(duì)數(shù)據(jù)的存儲(chǔ)布局進(jìn)行對(duì)齊優(yōu)化,本文主要介紹了golang 內(nèi)存對(duì)齊的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-08-08Go語(yǔ)言下載網(wǎng)絡(luò)圖片或文件的方法示例
這篇文章主要介紹了Go語(yǔ)言下載網(wǎng)絡(luò)圖片或文件的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12