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

用Go+Redis實(shí)現(xiàn)分布式鎖的示例代碼

 更新時間:2021年12月17日 10:10:16   作者:kevinwan  
在分布式的業(yè)務(wù)中 , 如果有的共享資源需要安全的被訪問和處理 , 那就需要分布式鎖,本文主要介紹了用Go+Redis實(shí)現(xiàn)分布式鎖的示例代碼,感興趣的可以了解一下

為什么需要分布式鎖

用戶下單
鎖住 uid,防止重復(fù)下單。

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

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

分布式鎖需要具備特性

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

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

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

高性能高可用
鎖是代碼運(yùn)行的關(guān)鍵前置節(jié)點(diǎn),一旦不可用則業(yè)務(wù)直接就報(bào)故障了。高并發(fā)場景下,高性能高可用是基本要求。

實(shí)現(xiàn) Redis 鎖應(yīng)先掌握哪些知識點(diǎn)

set 命令

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

Redis.lua 腳本

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

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

core/stores/redis/redislock.go

加鎖流程

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

else
    -- 鎖key.value不等于傳入的value則說明是第一次獲取鎖
    -- SET key value NX PX timeout : 當(dāng)key不存在時才設(shè)置key的值
    -- 設(shè)置成功會自動返回“OK”,設(shè)置失敗返回“NULL Bulk Reply”
    -- 為什么這里要加“NX”呢,因?yàn)樾枰乐拱褎e人的鎖給覆蓋了
    return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
end

解鎖流程

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

源碼解析

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
    // 默認(rèn)超時時間,防止死鎖
    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,
        // 獲取鎖時,鎖的值通過隨機(jī)字符串生成
        // 實(shí)際上go-zero提供更加高效的隨機(jī)字符串生成方式
        // 見core/stringx/random.go:Randn
        id:    randomStr(randomLen),
    }
}

// Acquire acquires the lock.
// 加鎖
func (rl *RedisLock) Acquire() (bool, error) {
    // 獲取過期時間
    seconds := atomic.LoadUint32(&rl.seconds)
    // 默認(rèn)鎖過期時間為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()之前調(diào)用
// 不然默認(rèn)為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)
}

關(guān)于分布式鎖還有哪些實(shí)現(xiàn)方案

etcd
redis redlock

項(xiàng)目地址

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

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

相關(guān)文章

  • Go語言獲取本機(jī)邏輯CPU數(shù)量的方法

    Go語言獲取本機(jī)邏輯CPU數(shù)量的方法

    這篇文章主要介紹了Go語言獲取本機(jī)邏輯CPU數(shù)量的方法,實(shí)例分析了runtime庫的操作技巧,需要的朋友可以參考下
    2015-03-03
  • Go中的nil切片和空切片區(qū)別詳解

    Go中的nil切片和空切片區(qū)別詳解

    這篇文章主要介紹了Go中的nil切片和空切片區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Go反射中type和kind區(qū)別比較詳析

    Go反射中type和kind區(qū)別比較詳析

    這篇文章主要給大家介紹了關(guān)于Go反射中type和kind區(qū)別比較的相關(guān)資料,Type是接口類型,Value是Struct類型,Type是類型描述,而Value是具體的值,需要的朋友可以參考下
    2023-10-10
  • 深入學(xué)習(xí)Golang并發(fā)編程必備利器之sync.Cond類型

    深入學(xué)習(xí)Golang并發(fā)編程必備利器之sync.Cond類型

    Go?語言的?sync?包提供了一系列同步原語,其中?sync.Cond?就是其中之一。本文將深入探討?sync.Cond?的實(shí)現(xiàn)原理和使用方法,幫助大家更好地理解和應(yīng)用?sync.Cond,需要的可以參考一下
    2023-05-05
  • Ubuntu安裝Go語言運(yùn)行環(huán)境

    Ubuntu安裝Go語言運(yùn)行環(huán)境

    由于最近偏愛Ubuntu,在加上作為一門開源語言,在Linux上從源代碼開始搭建環(huán)境更讓人覺得有趣味性。讓我們直接先從Go語言的環(huán)境搭建開始
    2015-04-04
  • GO語言 復(fù)合類型專題

    GO語言 復(fù)合類型專題

    這篇文章主要介紹了GO語言 復(fù)合類型的的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • Go 面向包新提案透明文件夾必要性分析

    Go 面向包新提案透明文件夾必要性分析

    這篇文章主要為大家介紹了Go 面向包新提案,透明文件夾必要性分析,看看是否合適加進(jìn) Go 特性中,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • GoLang基礎(chǔ)學(xué)習(xí)之go?test測試

    GoLang基礎(chǔ)學(xué)習(xí)之go?test測試

    相信每位編程開發(fā)者們應(yīng)該都知道,Golang作為一門標(biāo)榜工程化的語言,提供了非常簡便、實(shí)用的編寫單元測試的能力,下面這篇文章主要給大家介紹了關(guān)于GoLang基礎(chǔ)學(xué)習(xí)之go?test測試的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • GoLang與Java各自生成grpc代碼流程介紹

    GoLang與Java各自生成grpc代碼流程介紹

    這篇文章主要介紹了GoLang與Java各自生成grpc代碼流程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-03-03
  • 詳解如何在Go服務(wù)中做鏈路追蹤

    詳解如何在Go服務(wù)中做鏈路追蹤

    使用 Go 語言開發(fā)微服務(wù)的時候,需要追蹤每一個請求的訪問鏈路,本文主要介紹了如何在Go 服務(wù)中做鏈路追蹤,感興趣的可以了解一下
    2021-09-09

最新評論