溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

etcd與分布式鎖的介紹

發布時間:2021-06-22 14:06:13 來源:億速云 閱讀:280 作者:chen 欄目:web開發

本篇內容主要講解“etcd與分布式鎖的介紹”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“etcd與分布式鎖的介紹”吧!

1. 實現分布式鎖的組件們

在分布式系統中,常用于實現分布式鎖的組件有:Redis、zookeeper、etcd,下面針對各自的特性進行對比:

etcd與分布式鎖的介紹

由上圖可以看出三種組件各自的特點,其中對于分布式鎖來說至關重要的一點是要求CP。但是,Redis集群卻不支持CP,而是支持AP。雖然,官方也給出了redlock的方案,但由于需要部署多個實例(超過一半實例成功才視為成功),部署、維護比較復雜。所以在對一致性要求很高的業務場景下(電商、銀行支付),一般選擇使用zookeeper或者etcd。對比zookeeper與etcd,如果考慮性能、并發量、維護成本來看。由于etcd是用Go語言開發,直接編譯為二進制可執行文件,并不依賴其他任何東西,則更具有優勢。本文,則選擇etcd來討論某些觀點。

2. 對于分布式鎖來說AP為什么不好

在CAP理論中,由于分布式系統中多節點通信不可避免出現網絡延遲、丟包等問題一定會造成網絡分區,在造成網絡分區的情況下,一般有兩個選擇:CP or  AP。

①  選擇AP模型實現分布式鎖時,client在通過集群主節點加鎖成功之后,則立刻會獲取鎖成功的反饋。此時,在主節點還沒來得及把數據同步給從節點時發生down機的話,系統會在從節點中選出一個節點作為新的主節點,新的主節點沒有老的主節點對應的鎖數據,導致其他client可以在新的主節點上拿到相同的鎖。這個時候,就會導致多個進程/線程/協程來操作相同的臨界資源數據,從而引發數據不一致性等問題。

②  選擇CP模型實現分布式鎖,只有在主節點把數據同步給大于1/2的從節點之后才被視為加鎖成功。此時,主節點由于某些原因down機,系統會在從節點中選取出來數據比較新的一個從節點作為新的主節點,從而避免數據丟失等問題。

所以,對于分布式鎖來說,在對數據有強一致性要求的場景下,AP模型不是一個好的選擇。如果可以容忍少量數據丟失,出于維護成本等因素考慮,AP模型的Redis可優先選擇。

3. 分布式鎖的特點以及操作

對于分布式鎖來說,操作的動作包含:

  1. 獲取鎖

  2. 釋放鎖

  3. 業務處理過程中過程中,另起線程/協程進行鎖的續約

etcd與分布式鎖的介紹

4. 關于etcd

etcd與分布式鎖的介紹

官方文檔永遠是最好的學習資料,官方介紹etcd如是說:

  • 分布式系統使用etcd作為配置管理、服務發現和協調分布式工作的一致鍵值存儲。許多組織使用etcd來實現生產系統,如容器調度器、服務發現服務和分布式數據存儲。使用etcd的常見分布式模式包括leader選舉、分布式鎖和監視機器活動。

  • Distributed systems use etcd as a consistent key-value store for  configuration management, service discovery, and coordinating distributed work.  Many organizations use etcd to implement production systems such as container  schedulers, service discovery services, and distributed data storage. Common  distributed patterns using etcd include leader election, distributed locks, and  monitoring machine liveness.

  • https://etcd.io/docs/v3.4/learning/why/

分布式鎖僅是etcd可以實現眾多功能中的一項,服務注冊與發現在etcd中用的則會更多。

官方也對眾多組件進行了對比,并整理如下:

etcd與分布式鎖的介紹

通過對比可以看出各自的特點,至于具體選擇哪一款,你心中可能也有了自己的答案。

5. etcd實現分布式鎖的相關接口

對于分布式鎖,主要用到etcd對應的添加、刪除、續約接口。

// KV:鍵值相關操作 type KV interface {     // 存放.     Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error)     // 獲取.     Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error)     // 刪除.     Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error)     // 壓縮rev指定版本之前的歷史數據.     Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error)     // 通用的操作執行命令,可用于操作集合的遍歷。Put/Get/Delete也是基于Do.     Do(ctx context.Context, op Op) (OpResponse, error)     // 創建一個事務,只支持If/Then/Else/Commit操作.     Txn(ctx context.Context) Txn }   // Lease:租約相關操作 type Lease interface {     // 分配一個租約.     Grant(ctx context.Context, ttl int64) (*LeaseGrantResponse, error)     // 釋放一個租約.     Revoke(ctx context.Context, id LeaseID) (*LeaseRevokeResponse, error)     // 獲取剩余TTL時間.     TimeToLive(ctx context.Context, id LeaseID, opts ...LeaseOption) (*LeaseTimeToLiveResponse, error)     // 獲取所有租約.     Leases(ctx context.Context) (*LeaseLeasesResponse, error)     // 續約保持激活狀態.     KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error)     // 僅續約激活一次.     KeepAliveOnce(ctx context.Context, id LeaseID) (*LeaseKeepAliveResponse, error)     // 關閉續約激活的功能.     Close() error }

 6. etcd實現分布式鎖代碼示例

package main  import (     "context"     "fmt"     "go.etcd.io/etcd/clientv3"     "time" )  var conf clientv3.Config  // 鎖結構體 type EtcdMutex struct {     Ttl int64//租約時間      Conf   clientv3.Config    //etcd集群配置     Key    string//etcd的key     cancel context.CancelFunc //關閉續租的func      txn     clientv3.Txn     lease   clientv3.Lease     leaseID clientv3.LeaseID }  // 初始化鎖 func (em *EtcdMutex) init() error {     var err error     var ctx context.Context      client, err := clientv3.New(em.Conf)     if err != nil {         return err     }      em.txn = clientv3.NewKV(client).Txn(context.TODO())     em.lease = clientv3.NewLease(client)     leaseResp, err := em.lease.Grant(context.TODO(), em.Ttl)      if err != nil {         return err     }      ctx, em.cancel = context.WithCancel(context.TODO())     em.leaseID = leaseResp.ID     _, err = em.lease.KeepAlive(ctx, em.leaseID)      return err }  // 獲取鎖 func (em *EtcdMutex) Lock() error {     err := em.init()     if err != nil {         return err     }      // LOCK     em.txn.If(clientv3.Compare(clientv3.CreateRevision(em.Key), "=", 0)).         Then(clientv3.OpPut(em.Key, "", clientv3.WithLease(em.leaseID))).Else()      txnResp, err := em.txn.Commit()     if err != nil {         return err     }      // 判斷txn.if條件是否成立     if !txnResp.Succeeded {         return fmt.Errorf("搶鎖失敗")     }      returnnil }  //釋放鎖 func (em *EtcdMutex) UnLock() {     // 租約自動過期,立刻過期     // cancel取消續租,而revoke則是立即過期     em.cancel()     em.lease.Revoke(context.TODO(), em.leaseID)      fmt.Println("釋放了鎖") }  // groutine1 func try2lock1() {     eMutex1 := &EtcdMutex{         Conf: conf,         Ttl:  10,         Key:  "lock",     }      err := eMutex1.Lock()     if err != nil {         fmt.Println("groutine1搶鎖失敗")         return     }     defer eMutex1.UnLock()      fmt.Println("groutine1搶鎖成功")     time.Sleep(10 * time.Second) }  // groutine2 func try2lock2() {     eMutex2 := &EtcdMutex{         Conf: conf,         Ttl:  10,         Key:  "lock",     }      err := eMutex2.Lock()     if err != nil {         fmt.Println("groutine2搶鎖失敗")         return     }      defer eMutex2.UnLock()     fmt.Println("groutine2搶鎖成功") }  // 測試代碼 func EtcdRunTester() {     conf = clientv3.Config{         Endpoints:   []string{"127.0.0.1:2379"},         DialTimeout: 5 * time.Second,     }      // 啟動兩個協程競爭鎖     go try2lock1()     go try2lock2()      time.Sleep(300 * time.Second) }

 總結

可以提供分布式鎖功能的組件有多種,但是每一種都有自己的脾氣與性格。至于選擇哪一種組件,則要看數據對業務的重要性,數據要求強一致性推薦支持CP的etcd、zookeeper,數據允許少量丟失、不要求強一致性的推薦支持AP的Redis。

到此,相信大家對“etcd與分布式鎖的介紹”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女