一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Golang - 用Go+Redis實現分布式鎖的示例代碼

用Go+Redis實現分布式鎖的示例代碼

2022-01-24 00:50kevinwan Golang

在分布式的業務中 , 如果有的共享資源需要安全的被訪問和處理 , 那就需要分布式鎖,本文主要介紹了用Go+Redis實現分布式鎖的示例代碼,感興趣的可以了解一下

為什么需要分布式鎖

用戶下單
鎖住 uid,防止重復下單。

庫存扣減
鎖住庫存,防止超賣。

余額扣減
鎖住賬戶,防止并發操作。
分布式系統中共享同一個資源時往往需要分布式鎖來保證變更資源一致性。

 

分布式鎖需要具備特性

排他性
鎖的基本特性,并且只能被第一個持有者持有。

防死鎖
高并發場景下臨界資源一旦發生死鎖非常難以排查,通常可以通過設置超時時間到期自動釋放鎖來規避。

可重入
鎖持有者支持可重入,防止鎖持有者再次重入時鎖被超時釋放。

高性能高可用
鎖是代碼運行的關鍵前置節點,一旦不可用則業務直接就報故障了。高并發場景下,高性能高可用是基本要求。

 

實現 Redis 鎖應先掌握哪些知識點

set 命令

SET key value [EX seconds] [PX milliseconds] [NX|XX]
  • EX second :設置鍵的過期時間為 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
  • PX millisecond :設置鍵的過期時間為 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
  • NX :只在鍵不存在時,才對鍵進行設置操作。 SET key value NX 效果等同于 SETNX key value 。
  • XX :只在鍵已經存在時,才對鍵進行設置操作。

Redis.lua 腳本

使用 redis lua 腳本能將一系列命令操作封裝成 pipline 實現整體操作的原子性。

 

go-zero 分布式鎖 RedisLock 源碼分析

core/stores/redis/redislock.go

加鎖流程

-- KEYS[1]: 鎖key
-- ARGV[1]: 鎖value,隨機字符串
-- ARGV[2]: 過期時間
-- 判斷鎖key持有的value是否等于傳入的value
-- 如果相等說明是再次獲取鎖并更新獲取時間,防止重入時過期
-- 這里說明是“可重入鎖”
if redis.call("GET", KEYS[1]) == ARGV[1] then
  -- 設置
  redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
  return "OK"

else
  -- 鎖key.value不等于傳入的value則說明是第一次獲取鎖
  -- SET key value NX PX timeout : 當key不存在時才設置key的值
  -- 設置成功會自動返回“OK”,設置失敗返回“NULL Bulk Reply”
  -- 為什么這里要加“NX”呢,因為需要防止把別人的鎖給覆蓋了
  return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
end

用Go+Redis實現分布式鎖的示例代碼

解鎖流程

-- 釋放鎖
-- 不可以釋放別人的鎖
if redis.call("GET", KEYS[1]) == ARGV[1] then
  -- 執行成功返回“1”
  return redis.call("DEL", KEYS[1])
else
  return 0
end

用Go+Redis實現分布式鎖的示例代碼

源碼解析

package redis

import (
  "math/rand"
  "strconv"
  "sync/atomic"
  "time"

  red "github.com/go-redis/redis"
  "github.com/tal-tech/go-zero/core/logx"
)

const (
  letters     = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  lockCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
  redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
  return "OK"
else
  return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
end`
  delCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
  return redis.call("DEL", KEYS[1])
else
  return 0
end`
  randomLen = 16
  // 默認超時時間,防止死鎖
  tolerance       = 500 // milliseconds
  millisPerSecond = 1000
)

// A RedisLock is a redis lock.
type RedisLock struct {
  // redis客戶端
  store *Redis
  // 超時時間
  seconds uint32
  // 鎖key
  key string
  // 鎖value,防止鎖被別人獲取到
  id string
}

func init() {
  rand.Seed(time.Now().UnixNano())
}

// NewRedisLock returns a RedisLock.
func NewRedisLock(store *Redis, key string) *RedisLock {
  return &RedisLock{
      store: store,
      key:   key,
      // 獲取鎖時,鎖的值通過隨機字符串生成
      // 實際上go-zero提供更加高效的隨機字符串生成方式
      // 見core/stringx/random.go:Randn
      id:    randomStr(randomLen),
  }
}

// Acquire acquires the lock.
// 加鎖
func (rl *RedisLock) Acquire() (bool, error) {
  // 獲取過期時間
  seconds := atomic.LoadUint32(&rl.seconds)
  // 默認鎖過期時間為500ms,防止死鎖
  resp, err := rl.store.Eval(lockCommand, []string{rl.key}, []string{
      rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance),
  })
  if err == red.Nil {
      return false, nil
  } else if err != nil {
      logx.Errorf("Error on acquiring lock for %s, %s", rl.key, err.Error())
      return false, err
  } else if resp == nil {
      return false, nil
  }

  reply, ok := resp.(string)
  if ok && reply == "OK" {
      return true, nil
  }

  logx.Errorf("Unknown reply when acquiring lock for %s: %v", rl.key, resp)
  return false, nil
}

// Release releases the lock.
// 釋放鎖
func (rl *RedisLock) Release() (bool, error) {
  resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id})
  if err != nil {
      return false, err
  }

  reply, ok := resp.(int64)
  if !ok {
      return false, nil
  }

  return reply == 1, nil
}

// SetExpire sets the expire.
// 需要注意的是需要在Acquire()之前調用
// 不然默認為500ms自動釋放
func (rl *RedisLock) SetExpire(seconds int) {
  atomic.StoreUint32(&rl.seconds, uint32(seconds))
}

func randomStr(n int) string {
  b := make([]byte, n)
  for i := range b {
      b[i] = letters[rand.Intn(len(letters))]
  }
  return string(b)
}

 

關于分布式鎖還有哪些實現方案

etcd 
redis redlock

項目地址

https://github.com/zeromicro/go-zero

到此這篇關于用Go+Redis實現分布式鎖的示例代碼的文章就介紹到這了,更多相關Go Redis分布式鎖內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://segmentfault.com/a/1190000041120021

延伸 · 閱讀

精彩推薦
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • Golanggo日志系統logrus顯示文件和行號的操作

    go日志系統logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

    這篇文章主要介紹了Golang通脈之數據類型,在編程語言中標識符就是定義的具有某種意義的詞,比如變量名、常量名、函數名等等,Go語言中標識符允許由...

    4272021-11-24
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

    這篇文章主要介紹了Golang中Bit數組的實現方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨尊11682021-06-09
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

    這篇文章主要給大家介紹了關于golang的httpserver優雅重啟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    helight2992020-05-14
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細介紹,從例子說起,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網11352020-05-21
主站蜘蛛池模板: 亚洲精品tv久久久久久久久久 | 男人天堂新 | 成人150p| 精精国产www视频在线观看免费 | 办公室大战秘书呻吟 | 青青草原伊人网 | 男同gay作爰视频网站 | 国产成人精品免费 | 1024国产精品视频观看 | 精品久久99麻豆蜜桃666 | 国产女乱淫真高清免费视频 | 精品网站 | 国产午夜亚洲精品理论片不卡 | 日韩欧美一区二区在线观看 | 久久综合狠狠综合久久综合88 | 国产精品合集一区二区 | 色噜噜亚洲男人的天堂www | 丝袜性爱 | 亚洲国产第一区二区香蕉日日 | 国产成人精品一区二区不卡 | 美女乳液| 美女把小内内脱个精光打屁屁 | 成年性香蕉漫画在线观看 | 精品高潮呻吟99AV无码视频 | 久久毛片免费看一区二区三区 | 久久r视频| 白鹿扒开内裤露出尿孔 | 亚洲精品资源在线 | futa百合高肉全h | 色批网站www | 日本动漫打扑克动画片樱花动漫 | 天天曰天天干 | 春宵福利网站在线观看 | 四虎精品在线观看 | 欧美猛男同志同性video | 国产亚洲欧美在线中文bt天堂网 | 我被黑人彻底征服的全文 | 拍拍叫痛的无挡视频免费 | 女王调奴丨vk | 国产一级一级一级成人毛片 | 四虎麻豆国产精品 |