亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Golang分布式鎖簡單案例實現(xiàn)流程

 更新時間:2022年12月14日 15:39:46   作者:kina100  
分布式鎖是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。如果不同的系統(tǒng)或是同一個系統(tǒng)的不同主機之間共享了一個或一組資源,那么訪問這些資源時,需要通過一些互斥手段來防止彼此之間的干擾以保證一致性,在這種情況下,就需要使用分布式鎖了

其實鎖這種東西,都能能不加就不加,鎖會導致程序一定程度上退回到串行化,進而降低效率。

首先,看一個案例,如果要實現(xiàn)一個計數(shù)器,并且是多個協(xié)程共同進行的,就會出現(xiàn)以下的情況:

package main
import (
   "fmt"
   "sync"
)
func main() {
   numberFlag := 0
   wg := new(sync.WaitGroup)
   for i := 0; i < 200; i++ {
      wg.Add(1)
      go func() {
         defer wg.Done()
         numberFlag++
      }()
   }
   fmt.Println(numberFlag)
   wg.Wait()
}

每次執(zhí)行后的計數(shù)器結(jié)果都是不同的,這是因為計數(shù)器本身是被不同的協(xié)程搶著+1,會產(chǎn)生多個協(xié)程同時拿到numberFlag=N的情況。為了避免這種資源競爭,要對資源進行加鎖,使得同一時刻只有一個協(xié)程能對資源進行操控。

package main
import (
   "fmt"
   "sync"
)
func main() {
   numberFlag := 0
   myLock := make(chan struct{}, 1)
   wg := new(sync.WaitGroup)
   for i := 0; i < 200; i++ {
      wg.Add(1)
      go func() {
         defer func() {
            // 釋放鎖
            <-myLock
         }()
         defer wg.Done()
         // 搶鎖
         myLock <- struct{}{}
         numberFlag++
      }()
   }
   wg.Wait()
   fmt.Println(numberFlag)
}

但是這種鎖只能用于你自己的本地服務,一旦出現(xiàn)多服務,比如分布式,微服務,這樣的場景,這個鎖就沒啥用了,這就需要分布式鎖。

關(guān)于分布式鎖,一般的實現(xiàn)就是用redis或者zookeeper實現(xiàn)。redis比較方便的就是大部分的服務都會使用redis,無需額外安裝依賴,而zookeeper普通服務用的并不多,即使是kafka也在新版放棄了zookeeper。

zookeeper最大的好處就是可以通過心跳檢測客戶端的情況,進而避免重復得鎖的問題。

但是同時也產(chǎn)生了一些問題,這個心跳檢測多久一次,在心跳檢測的間隔如果出現(xiàn)了鎖超時的問題怎么辦,等等。

所以一些服務還是傾向于使用redis來實現(xiàn)分布式鎖。

package main
import (
   "fmt"
   "github.com/gomodule/redigo/redis"
   "go-face-test/redisTest/redisOne/redisConn"
   "sync"
   "time"
)
func main() {
   // 分布式鎖
   var LockName = "lockLock"
   // 十秒過期時間
   var ExpirationTime = 10
   wg := new(sync.WaitGroup)
   wg.Add(2)
   // 起兩個協(xié)程來模擬分布式服務的搶占
   go handleBusiness(LockName, ExpirationTime, "A", wg)
   go handleBusiness(LockName, ExpirationTime, "B", wg)
   wg.Wait()
}
func handleBusiness(lockName string, ExpTime int, nowGroName string, wg *sync.WaitGroup) {
   // One服務獲取鎖是否存在
   c := redisConn.Get()
   defer c.Close()
   for {
      isKeyExists, err := redis.Bool(c.Do("EXISTS", lockName))
      if err != nil {
         fmt.Println("err while checking keys:", err)
      } else {
         fmt.Println(isKeyExists)
      }
      if isKeyExists {
         // 存在這把鎖,開始自旋等待
         fmt.Println("當前協(xié)程為: " + nowGroName + " 沒搶到鎖……")
         //休息1s
         time.Sleep(time.Second)
      } else {
         // 設置一把鎖
         // 值為1,過期時間為10秒
         reply, err := c.Do("SET", lockName, 2, "EX", ExpTime, "NX")
         // 搶占失敗
         if reply == nil {
            fmt.Println("當前協(xié)程為: " + nowGroName + " 搶占鎖失敗")
            continue
         }
         // 開始業(yè)務處理
         fmt.Println("當前協(xié)程為: " + nowGroName + " 啊啊啊啊。這是一個業(yè)務處理,預計處理時間為 3s 。處理開始........")
         fmt.Println("當前協(xié)程為: " + nowGroName + " 距離處理完成還有---3s" + time.Now().Format("2006-01-02 15:04:05"))
         time.Sleep(time.Second)
         fmt.Println("當前協(xié)程為: " + nowGroName + " 距離處理完成還有---2s" + time.Now().Format("2006-01-02 15:04:05"))
         time.Sleep(time.Second)
         fmt.Println("當前協(xié)程為: " + nowGroName + " 距離處理完成還有---1s" + time.Now().Format("2006-01-02 15:04:05"))
         time.Sleep(time.Second)
         //業(yè)務結(jié)束,釋放鎖
         _, err = c.Do("DEL", lockName)
         if err != nil {
            fmt.Println("err while deleting:", err)
         }
         break
      }
   }
   wg.Done()
}

但是這個鎖明顯有問題:

第一,當A服務(本案例中其實是協(xié)程模擬的)拿到鎖之后,處理超時了,鎖還沒有釋放,就已經(jīng)過期,過期后B服務就搶到了鎖,此時AB均認為自己拿到了鎖

第二,A服務按理說只能去掉自己的服務加上的鎖,如果不止是有AB兩個服務,有更多的服務,那么A如果出現(xiàn)處理較慢,鎖超時后,B服務搶到鎖,A又處理完成所有的事釋放了鎖,那其實是釋放掉了B的鎖。也就是說,釋放鎖的時候也必須判斷是否是自己的鎖

那么就得用redis的lua來保證原子性

package main
import (
   "fmt"
   "github.com/gomodule/redigo/redis"
   "go-face-test/redisTest/redisTwo/redisConn"
   "log"
   "math/rand"
   "strconv"
   "sync"
   "time"
)
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
var lockCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
    redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2] , "NX")
    return "OK"
else
    return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
end`
var delCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end`
func main() {
   // 分布式鎖
   var LockName = "lockLock"
   // 十秒過期時間
   var ExpirationTime = 3
   wg := new(sync.WaitGroup)
   wg.Add(2)
   // 起兩個協(xié)程來模擬分布式服務的搶占
   go handleBusiness(LockName, ExpirationTime, "A", wg)
   go handleBusiness(LockName, ExpirationTime, "B", wg)
   wg.Wait()
}
func init() {
   rand.Seed(time.Now().UnixNano())
}
func handleBusiness(lockName string, ExpTime int, nowGroName string, wg *sync.WaitGroup) {
   // One服務獲取鎖是否存在
   c := redisConn.Get()
   defer c.Close()
   for {
      isKeyExists, err := redis.Bool(c.Do("EXISTS", lockName))
      if err != nil {
         fmt.Println("err while checking keys:", err)
      } else {
         fmt.Println(isKeyExists)
      }
      if isKeyExists {
         // 存在這把鎖,開始自旋等待
         fmt.Println("當前協(xié)程為: " + nowGroName + " 沒搶到鎖……")
         //休息1s
         time.Sleep(time.Second)
      } else {
         // 設置一把鎖
         // 鎖的值是根據(jù)當前服務名稱和時間來的
         lockFlag, lockValue, _ := getLock(lockName, nowGroName, ExpTime, c)
         // 搶占失敗
         if !lockFlag {
            fmt.Println("當前協(xié)程為: " + nowGroName + " 搶占鎖失敗")
            continue
         }
         // 開始業(yè)務處理
         fmt.Println("當前協(xié)程為: " + nowGroName + " 啊啊啊啊。這是一個業(yè)務處理,預計處理時間為 " + strconv.Itoa(ExpTime) + "s 。處理開始........")
         for i := ExpTime - 1; i > 0; i-- {
            fmt.Println("當前協(xié)程為: " + nowGroName + " 距離處理完成還有---" + strconv.Itoa(i) + "s " + time.Now().Format("2006-01-02 15:04:05"))
            time.Sleep(time.Second)
         }
         //業(yè)務結(jié)束,釋放鎖
         lockDelFlag, _ := delLock(lockName, lockValue, c)
         //獲取當前鎖的值
         if lockDelFlag {
            fmt.Println("釋放鎖成功")
         } else {
            fmt.Println("這個鎖不是你的,或者這個鎖已經(jīng)超時")
         }
         break
      }
   }
   wg.Done()
}
// 獲得唯一鎖的值
func getLockOnlyValue(nowGroName string) string {
   nano := strconv.FormatInt(time.Now().UnixNano(), 10)
   return nowGroName + "_" + nano + "_" + RandStringRunes(6)
}
// 獲得一個鎖
func getLock(LockName, nowGroName string, timeOut int, conn redis.Conn) (bool, string, error) {
   myLockValue := getLockOnlyValue(nowGroName)
   lua := redis.NewScript(1, lockCommand)
   resp, err := lua.Do(conn, LockName, myLockValue, strconv.Itoa(timeOut*1000))
   if err != nil {
      log.Fatal(LockName, err)
      return false, "", err
   } else if resp == nil {
      return false, "", nil
   }
   s, ok := resp.(string)
   if !ok {
      return false, "", nil
   }
   if s != "OK" {
      return false, "", nil
   }
   return true, myLockValue, nil
}
// 刪除一個鎖
func delLock(LockName, LockeValue string, conn redis.Conn) (bool, error) {
   lua := redis.NewScript(1, delCommand)
   resp, err := lua.Do(conn, LockName, LockeValue)
   if err != nil {
      return false, err
   }
   reply, ok := resp.(int64)
   if !ok {
      return false, nil
   }
   return reply == 1, nil
}
func RandStringRunes(n int) string {
   b := make([]rune, n)
   for i := range b {
      b[i] = letterRunes[rand.Intn(len(letterRunes))]
   }
   return string(b)
}

到此這篇關(guān)于Golang分布式鎖簡單案例實現(xiàn)流程的文章就介紹到這了,更多相關(guān)Go分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言中的方法、接口和嵌入類型詳解

    Go語言中的方法、接口和嵌入類型詳解

    這篇文章主要介紹了Go語言中的方法、接口和嵌入類型詳解,本文分別對它們做了詳細講解,需要的朋友可以參考下
    2014-10-10
  • 如何編寫Go語言中間件的實例教程

    如何編寫Go語言中間件的實例教程

    不知道大家有沒有寫過中間件呢,它是怎么寫的呢?下面這篇文中就來給大家分享一下使用Go,如何編寫中間件,文中通過示例代碼介紹的非常詳細,供大家參考學習,下面隨著小編來一起學習學習吧。
    2018-04-04
  • 一文詳解Golang使用接口支持Apply方法的配置模式

    一文詳解Golang使用接口支持Apply方法的配置模式

    這篇文章主要為大家介紹了一文詳解Golang使用接口支持Apply方法的配置模式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01
  • Go調(diào)用opencv實現(xiàn)圖片矯正的代碼示例

    Go調(diào)用opencv實現(xiàn)圖片矯正的代碼示例

    這篇文章主要為大家詳細介紹了Go調(diào)用opencv實現(xiàn)圖片矯正的代碼示例,文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-09-09
  • 超詳細Go語言中JSON處理技巧分享

    超詳細Go語言中JSON處理技巧分享

    這篇文章主要為大家總結(jié)了go語言中對JSON數(shù)據(jù)結(jié)構(gòu)和結(jié)構(gòu)體之間相互轉(zhuǎn)換問題及解決方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-06-06
  • Go疑難雜癥講解之為什么nil不等于nil

    Go疑難雜癥講解之為什么nil不等于nil

    在日常開發(fā)中,可能一不小心就會掉進?Go?語言的某些陷阱里,而本文要介紹的?nil?≠?nil?問題,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-10-10
  • golang監(jiān)聽ip數(shù)據(jù)包的實現(xiàn)步驟(golang純享版)

    golang監(jiān)聽ip數(shù)據(jù)包的實現(xiàn)步驟(golang純享版)

    這篇文章主要給大家介紹了golang監(jiān)聽ip數(shù)據(jù)包的實現(xiàn)步驟,本文以ip4 作為案例進行包抓取示范,ip6抓取與ip4方式異曲同工,可自行舉一反三得出,文中通過圖文結(jié)合給大家介紹的非常詳細,需要的朋友可以參考下
    2024-02-02
  • Go 微服務開發(fā)框架DMicro設計思路詳解

    Go 微服務開發(fā)框架DMicro設計思路詳解

    這篇文章主要為大家介紹了Go 微服務開發(fā)框架DMicro設計思路詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • Go實現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼

    Go實現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼

    本文主要介紹了Go實現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-07-07
  • golang新手不注意可能會出現(xiàn)的一些小問題

    golang新手不注意可能會出現(xiàn)的一些小問題

    最近在學習golang,發(fā)現(xiàn)了一些新手們需要注意的小問題,下面這篇文章主要給大家介紹了關(guān)于golang新手不注意可能會出現(xiàn)的一些小問題,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-12-12

最新評論