欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go?分布式鎖簡(jiǎn)單實(shí)現(xiàn)實(shí)例詳解

 更新時(shí)間:2022年09月15日 15:07:40   作者:水紋  
這篇文章主要為大家介紹了go?分布式鎖簡(jiǎn)單實(shí)現(xiàn)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

其實(shí)鎖這種東西,都能能不加就不加,鎖會(huì)導(dǎo)致程序一定程度上退回到串行化,進(jìn)而降低效率。

案例

首先,看一個(gè)案例,如果要實(shí)現(xiàn)一個(gè)計(jì)數(shù)器,并且是多個(gè)協(xié)程共同進(jìn)行的,就會(huì)出現(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í)行后的計(jì)數(shù)器結(jié)果都是不同的,這是因?yàn)橛?jì)數(shù)器本身是被不同的協(xié)程搶著+1,會(huì)產(chǎn)生多個(gè)協(xié)程同時(shí)拿到numberFlag=N的情況。為了避免這種資源競(jìng)爭(zhēng),要對(duì)資源進(jìn)行加鎖,使得同一時(shí)刻只有一個(gè)協(xié)程能對(duì)資源進(jìn)行操控。

資源加鎖

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)
}

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

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

zookeeper最大的好處就是可以通過(guò)心跳檢測(cè)客戶端的情況,進(jìn)而避免重復(fù)得鎖的問(wèn)題。
但是同時(shí)也產(chǎn)生了一些問(wèn)題,這個(gè)心跳檢測(cè)多久一次,在心跳檢測(cè)的間隔如果出現(xiàn)了鎖超時(shí)的問(wèn)題怎么辦,等等。

使用redis來(lái)實(shí)現(xiàn)分布式鎖

所以一些服務(wù)還是傾向于使用redis來(lái)實(shí)現(xiàn)分布式鎖。

package main
import (
   "fmt"
   "github.com/gomodule/redigo/redis"
   "go-face-test/redisTest/redisOne/redisConn"
   "sync"
   "time"
)
func main() {
   // 分布式鎖
   var LockName = "lockLock"
   // 十秒過(guò)期時(shí)間
   var ExpirationTime = 10
   wg := new(sync.WaitGroup)
   wg.Add(2)
   // 起兩個(gè)協(xié)程來(lái)模擬分布式服務(wù)的搶占
   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服務(wù)獲取鎖是否存在
   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 {
         // 存在這把鎖,開(kāi)始自旋等待
         fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 沒(méi)搶到鎖……")
         //休息1s
         time.Sleep(time.Second)
      } else {
         // 設(shè)置一把鎖
         // 值為1,過(guò)期時(shí)間為10秒
         reply, err := c.Do("SET", lockName, 2, "EX", ExpTime, "NX")
         // 搶占失敗
         if reply == nil {
            fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 搶占鎖失敗")
            continue
         }
         // 開(kāi)始業(yè)務(wù)處理
         fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 啊啊啊啊。這是一個(gè)業(yè)務(wù)處理,預(yù)計(jì)處理時(shí)間為 3s 。處理開(kāi)始........")
         fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 距離處理完成還有---3s" + time.Now().Format("2006-01-02 15:04:05"))
         time.Sleep(time.Second)
         fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 距離處理完成還有---2s" + time.Now().Format("2006-01-02 15:04:05"))
         time.Sleep(time.Second)
         fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 距離處理完成還有---1s" + time.Now().Format("2006-01-02 15:04:05"))
         time.Sleep(time.Second)
         //業(yè)務(wù)結(jié)束,釋放鎖
         _, err = c.Do("DEL", lockName)
         if err != nil {
            fmt.Println("err while deleting:", err)
         }
         break
      }
   }
   wg.Done()
}

但是這個(gè)鎖明顯有問(wèn)題:

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

第二,A服務(wù)按理說(shuō)只能去掉自己的服務(wù)加上的鎖,如果不止是有AB兩個(gè)服務(wù),有更多的服務(wù),那么A如果出現(xiàn)處理較慢,鎖超時(shí)后,B服務(wù)搶到鎖,A又處理完成所有的事釋放了鎖,那其實(shí)是釋放掉了B的鎖。

也就是說(shuō),釋放鎖的時(shí)候也必須判斷是否是自己的鎖

那么就得用redis的lua來(lái)保證原子性

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"
   // 十秒過(guò)期時(shí)間
   var ExpirationTime = 3
   wg := new(sync.WaitGroup)
   wg.Add(2)
   // 起兩個(gè)協(xié)程來(lái)模擬分布式服務(wù)的搶占
   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服務(wù)獲取鎖是否存在
   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 {
         // 存在這把鎖,開(kāi)始自旋等待
         fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 沒(méi)搶到鎖……")
         //休息1s
         time.Sleep(time.Second)
      } else {
         // 設(shè)置一把鎖
         // 鎖的值是根據(jù)當(dāng)前服務(wù)名稱和時(shí)間來(lái)的
         lockFlag, lockValue, _ := getLock(lockName, nowGroName, ExpTime, c)
         // 搶占失敗
         if !lockFlag {
            fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 搶占鎖失敗")
            continue
         }
         // 開(kāi)始業(yè)務(wù)處理
         fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 啊啊啊啊。這是一個(gè)業(yè)務(wù)處理,預(yù)計(jì)處理時(shí)間為 " + strconv.Itoa(ExpTime) + "s 。處理開(kāi)始........")
         for i := ExpTime - 1; i > 0; i-- {
            fmt.Println("當(dāng)前協(xié)程為: " + nowGroName + " 距離處理完成還有---" + strconv.Itoa(i) + "s " + time.Now().Format("2006-01-02 15:04:05"))
            time.Sleep(time.Second)
         }
         //業(yè)務(wù)結(jié)束,釋放鎖
         lockDelFlag, _ := delLock(lockName, lockValue, c)
         //獲取當(dāng)前鎖的值
         if lockDelFlag {
            fmt.Println("釋放鎖成功")
         } else {
            fmt.Println("這個(gè)鎖不是你的,或者這個(gè)鎖已經(jīng)超時(shí)")
         }
         break
      }
   }
   wg.Done()
}
// 獲得唯一鎖的值
func getLockOnlyValue(nowGroName string) string {
   nano := strconv.FormatInt(time.Now().UnixNano(), 10)
   return nowGroName + "_" + nano + "_" + RandStringRunes(6)
}
// 獲得一個(gè)鎖
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
}
// 刪除一個(gè)鎖
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)
}

以上就是go 分布式鎖簡(jiǎn)單實(shí)現(xiàn)實(shí)例詳解的詳細(xì)內(nèi)容,更多關(guān)于go 分布式鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go日志框架zap增強(qiáng)及源碼解讀

    Go日志框架zap增強(qiáng)及源碼解讀

    這篇文章主要為大家介紹了Go日志框架zap增強(qiáng)及源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • go語(yǔ)言中基本數(shù)據(jù)類型及應(yīng)用快速了解

    go語(yǔ)言中基本數(shù)據(jù)類型及應(yīng)用快速了解

    這篇文章主要為大家介紹了go語(yǔ)言中基本數(shù)據(jù)類型應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • GoFrame實(shí)現(xiàn)順序性校驗(yàn)示例詳解

    GoFrame實(shí)現(xiàn)順序性校驗(yàn)示例詳解

    這篇文章主要為大家介紹了GoFrame實(shí)現(xiàn)順序性校驗(yàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • go get 和 go install 對(duì)比介紹

    go get 和 go install 對(duì)比介紹

    go install和go get都是Go語(yǔ)言的工具命令,但它們之間有一些區(qū)別。go get:用于從遠(yuǎn)程代碼存儲(chǔ)庫(kù)(如 GitHub)中下載或更新Go代碼包。go install:用于編譯并安裝 Go 代碼包,本文go get和go install對(duì)比介紹的非常詳細(xì),需要的朋友可以參考一下
    2023-04-04
  • 使用Go開(kāi)發(fā)硬件驅(qū)動(dòng)程序的流程步驟

    使用Go開(kāi)發(fā)硬件驅(qū)動(dòng)程序的流程步驟

    Golang是一種簡(jiǎn)潔、高效的編程語(yǔ)言,它的強(qiáng)大并發(fā)性能和豐富的標(biāo)準(zhǔn)庫(kù)使得它成為了開(kāi)發(fā)硬件驅(qū)動(dòng)的理想選擇,在本文中,我們將探討如何使用Golang開(kāi)發(fā)硬件驅(qū)動(dòng)程序,并提供一個(gè)實(shí)例來(lái)幫助你入門,需要的朋友可以參考下
    2023-11-11
  • go解析YAML文件(多文檔解析)

    go解析YAML文件(多文檔解析)

    本文介紹了如何使用GO語(yǔ)言和client-go庫(kù)處理YAML文件,特別是在Kubernetes環(huán)境下,分析了YAML的特點(diǎn),如簡(jiǎn)潔性、易讀性、可嵌套性等,并展示了相關(guān)代碼實(shí)現(xiàn),包括單文檔和多文檔的處理方法,感興趣的可以了解一下
    2024-10-10
  • Go語(yǔ)言入門Go?Web?Fiber框架快速了解

    Go語(yǔ)言入門Go?Web?Fiber框架快速了解

    這篇文章主要為大家介紹了Go語(yǔ)言入門Go?Web?Fiber框架的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • Golang使用CGO與Plugin技術(shù)運(yùn)行加載C動(dòng)態(tài)庫(kù)

    Golang使用CGO與Plugin技術(shù)運(yùn)行加載C動(dòng)態(tài)庫(kù)

    這篇文章主要介紹了Golang使用CGO與Plugin技術(shù)運(yùn)行加載C動(dòng)態(tài)庫(kù),Golang?程序在運(yùn)行時(shí)加載C動(dòng)態(tài)庫(kù)的技術(shù),跳過(guò)了Golang項(xiàng)目編譯階段需要鏈接C動(dòng)態(tài)庫(kù)的過(guò)程,提高了Golang項(xiàng)目開(kāi)發(fā)部署的靈活性
    2022-07-07
  • GO語(yǔ)言利用K近鄰算法實(shí)現(xiàn)小說(shuō)鑒黃

    GO語(yǔ)言利用K近鄰算法實(shí)現(xiàn)小說(shuō)鑒黃

    本文給大家分享的是一段GO語(yǔ)言利用K近鄰算法實(shí)現(xiàn)小說(shuō)鑒黃的方法,本方法的鑒別的關(guān)鍵是關(guān)鍵是向量點(diǎn)的選擇和閾值的判定,推薦給大家,有需要的小伙伴可以參考下。
    2015-03-03
  • Go語(yǔ)言同步與異步執(zhí)行多個(gè)任務(wù)封裝詳解(Runner和RunnerAsync)

    Go語(yǔ)言同步與異步執(zhí)行多個(gè)任務(wù)封裝詳解(Runner和RunnerAsync)

    這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言同步與異步執(zhí)行多個(gè)任務(wù)封裝(Runner和RunnerAsync)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01

最新評(píng)論