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

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

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
主站蜘蛛池模板: 免费一级国产大片 | 91国内精品久久久久怡红院 | 美女18隐私羞羞视频网站 | 紧身裙女教师miad711在线 | 91久久福利国产成人精品 | 95在线观看精品视频 | 和老外3p爽粗大免费视频 | 国产中文字幕 | 久久黄视频 | 俄罗斯性高清完整版 | 午夜想想爱| 日韩一区国产二区欧美三 | 毛片大全高清免费 | 免费xxxxx大片在线观看影视 | 国产日产韩产麻豆1区 | 精品国产乱码久久久久久免费 | 国产高清不卡码一区二区三区 | 精品一区二区三区免费站 | 日韩基地1024首页 | 国产成人一区二区三区影院免费 | 夫妻性生活在线 | 欧美一级专区免费大片 | 美女被的视频 | 日本无吗免费一二区 | 操到翻白眼 | 国产美女亚洲精品久久久综合91 | 欧美一卡二卡科技有限公司 | 韩国黄色网址 | 免费看隐私美女 | 免费看美女被靠到爽的视频 | 日本乱子 | 久久亚洲精品AV成人无 | 久久精品热在线观看85 | 成人小视频在线免费观看 | 日本不卡一区二区三区在线观看 | 国产精品suv | 青青草视频国产 | 国产高清好大好夹受不了了 | 色老板在线 | 久久这里只有精品视频e | 强插美女|