最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語(yǔ)言如何使用分布式鎖解決并發(fā)問(wèn)題

 更新時(shí)間:2025年03月27日 09:42:25   作者:江湖十年  
這篇文章主要為大家詳細(xì)介紹了Go 語(yǔ)言生態(tài)中基于 Redis 實(shí)現(xiàn)的分布式鎖庫(kù) redsync,并探討其使用方法和實(shí)現(xiàn)原理,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

在分布式系統(tǒng)中,協(xié)調(diào)多個(gè)服務(wù)實(shí)例之間的共享資源訪問(wèn)是一個(gè)經(jīng)典的挑戰(zhàn)。傳統(tǒng)的單機(jī)鎖(如 sync.Mutex)無(wú)法實(shí)現(xiàn)跨進(jìn)程工作,此時(shí)就需要用到分布式鎖了。本文將介紹 Go 語(yǔ)言生態(tài)中基于 Redis 實(shí)現(xiàn)的分布式鎖庫(kù) redsync,并探討其使用方法和實(shí)現(xiàn)原理。

分布式鎖

首先我們來(lái)探討下為什么需要分布式鎖?當(dāng)我們編寫(xiě)的程序出現(xiàn)資源競(jìng)爭(zhēng)的時(shí)候,就需要使用互斥鎖來(lái)保證并發(fā)安全。而我們的服務(wù)很有可能不會(huì)單機(jī)部署,而是采用多副本的集群部署方案。無(wú)論哪種方案運(yùn)行程序,我們都需要合適的工具來(lái)解決并發(fā)問(wèn)題。在解決單個(gè)進(jìn)程間多個(gè)協(xié)程之間的并發(fā)資源搶占問(wèn)題時(shí),我們往往采用 sync.Mutex。而在解決多個(gè)進(jìn)程間的并發(fā)資源搶占問(wèn)題時(shí),就需要采用分布式鎖了,這就引出了我們今天要講解的 redsync。

為什么是 redsync

在 Go 中分布式鎖的開(kāi)源實(shí)現(xiàn)有很多,為什么選擇介紹和使用 redsync 呢?簡(jiǎn)單一句話:redsyncRedis 官方 唯一推薦的 Go Redis 分布式鎖解決方案,遵循 Redlock 算法。它允許在多個(gè)獨(dú)立 Redis 節(jié)點(diǎn)上創(chuàng)建高可用的鎖,適用于需要強(qiáng)一致性的分布式場(chǎng)景。

我們可以對(duì)比下 sync.Mutexredsync 之間的區(qū)別,讓你有個(gè)感性的認(rèn)識(shí)。

特性sync.Mutexredsync
適用范圍單個(gè)進(jìn)程內(nèi)的多個(gè) goroutine多個(gè)進(jìn)程(允許跨機(jī)器)
依賴無(wú)Redis
性能高(無(wú)網(wǎng)絡(luò)開(kāi)銷(xiāo))較低(涉及網(wǎng)絡(luò)通信)
實(shí)現(xiàn)復(fù)雜度簡(jiǎn)單較復(fù)雜(需處理網(wǎng)絡(luò)、超時(shí)等問(wèn)題)
典型場(chǎng)景內(nèi)存共享資源保護(hù)分布式系統(tǒng)共享資源保護(hù)

二者分別適用于不同的并發(fā)場(chǎng)景,選擇時(shí)需要根據(jù)實(shí)際需求(單機(jī)還是分布式)來(lái)決定。

redsync 快速上手

redsync 雖然內(nèi)部實(shí)現(xiàn)上比較復(fù)雜,但別被嚇到,它的用法超級(jí)簡(jiǎn)單。

示例代碼如下:

package main

import (
	"context"

	"github.com/go-redsync/redsync/v4"                  // 引入 redsync 庫(kù),用于實(shí)現(xiàn)基于 Redis 的分布式鎖
	"github.com/go-redsync/redsync/v4/redis/goredis/v9" // 引入 redsync 的 goredis 連接池
	goredislib "github.com/redis/go-redis/v9"           // 引入 go-redis 庫(kù),用于與 Redis 服務(wù)器通信
)

func main() {
	// 創(chuàng)建一個(gè) Redis 客戶端
	client := goredislib.NewClient(&goredislib.Options{
		Addr:     "localhost:36379", // Redis 服務(wù)器地址
		Password: "nightwatch",
	})

	// 使用 go-redis 客戶端創(chuàng)建一個(gè) redsync 連接池
	pool := goredis.NewPool(client)

	// 創(chuàng)建一個(gè) redsync 實(shí)例,用于管理分布式鎖
	rs := redsync.New(pool)

	// 創(chuàng)建一個(gè)名為 "test-redsync" 的互斥鎖(Mutex)
	mutex := rs.NewMutex("test-redsync")

	// 創(chuàng)建一個(gè)上下文(context),一般用于控制鎖的超時(shí)和取消
	ctx := context.Background()

	// 獲取鎖,如果獲取失?。ɡ珂i已被其他進(jìn)程持有),會(huì)返回錯(cuò)誤
	if err := mutex.LockContext(ctx); err != nil {
		panic(err) // 如果獲取鎖失敗,程序會(huì) panic
	}

	// TODO 執(zhí)行業(yè)務(wù)邏輯
	// ...

	// 釋放鎖,如果釋放失?。ɡ珂i已過(guò)期或不屬于當(dāng)前進(jìn)程),會(huì)返回錯(cuò)誤
	if _, err := mutex.UnlockContext(ctx); err != nil {
		panic(err) // 如果釋放鎖失敗,程序會(huì) panic
	}
}

因?yàn)?redsync 依賴 Redis,所以我們首先需要?jiǎng)?chuàng)建一個(gè) Redis 客戶端對(duì)象 client,調(diào)用 goredis.NewPool(client) 會(huì)基于這個(gè) client 創(chuàng)建一個(gè) redsync 的連接池,有了這個(gè)連接池 pool 就可以調(diào)用 redsync.New(pool) 創(chuàng)建一個(gè) redsync 實(shí)例來(lái)申請(qǐng)分布式鎖了。

redsync 提供了 NewMutex 方法可以創(chuàng)建一個(gè)分布式鎖,它接收一個(gè) name 參數(shù)作為鎖的名字,這個(gè)名字會(huì)作為 Redis 中的 key。

拿到鎖對(duì)象 mutex 以后,調(diào)用 mutex.LockContext(ctx) 就可以加鎖,加鎖后便可以訪問(wèn)競(jìng)態(tài)資源了,資源訪問(wèn)完成后,調(diào)用 mutex.UnlockContext(ctx) 便可以釋放鎖。

可以發(fā)現(xiàn),redsync 用法和 sync.Mutex 非常相似,核心就是 Lock/Unlock 兩個(gè)操作。redsync 的使用無(wú)非多了一步連接 Redis 的過(guò)程。

配置選項(xiàng)

不知道你有沒(méi)有想過(guò)一個(gè)問(wèn)題,我們?cè)谑褂?sync.Mutex 時(shí),如果某個(gè) gorutine 加鎖后不釋放掉,那么其他 gorutine 就無(wú)法獲取鎖,而在分布式場(chǎng)景中,如果一個(gè)進(jìn)程獲取了 Redis 分布式鎖,然后在未釋放鎖之前進(jìn)程掛掉了,其他進(jìn)程要如何獲取鎖呢,難道要一直等待下去嗎?

這里就要引出一個(gè)使用分布式鎖很重要的問(wèn)題,那就是一定要設(shè)置一個(gè)過(guò)期時(shí)間,這樣才能保證即使拿到鎖的進(jìn)程掛掉了,只要鎖的過(guò)期時(shí)間已到,鎖也一定會(huì)被自動(dòng)釋放掉,只有這樣,其他進(jìn)程才有機(jī)會(huì)獲取鎖。

而我們上面的示例中,之所以可以不設(shè)置鎖的過(guò)期時(shí)間,原因是 redsync 內(nèi)部設(shè)置了默認(rèn)值。以下是 redsyncNewMutex 方法的源碼:

// NewMutex returns a new distributed mutex with given name.
func (r *Redsync) NewMutex(name string, options ...Option) *Mutex {
	m := &Mutex{
		name:   name,
		expiry: 8 * time.Second,
		tries:  32,
		delayFunc: func(tries int) time.Duration {
			return time.Duration(rand.Intn(maxRetryDelayMilliSec-minRetryDelayMilliSec)+minRetryDelayMilliSec) * time.Millisecond
		},
		genValueFunc:  genValue,
		driftFactor:   0.01,
		timeoutFactor: 0.05,
		quorum:        len(r.pools)/2 + 1,
		pools:         r.pools,
	}
	for _, o := range options {
		o.Apply(m)
	}
	if m.shuffle {
		randomPools(m.pools)
	}
	return m
}

這里 Mutex 對(duì)象的第二個(gè)字段 expiry 就是分布式鎖的過(guò)期時(shí)間,這里默認(rèn)為設(shè)為 8 秒。tries 字段是獲取鎖的重試次數(shù),即嘗試獲取鎖失敗 32 次以后,才會(huì)返回加鎖失敗,因?yàn)榉植际綀?chǎng)景下失敗是很正常的情況,所以 32 次并不是一個(gè)很夸張的值。delayFunc 字段是每次失敗后重試的間隔時(shí)間。其他字段我就不一一講解了,絕大多數(shù)我們都用不到。

根據(jù)代碼我們很容易想到這幾個(gè)字段是通過(guò)選項(xiàng)模式來(lái)設(shè)置的。

  • WithExpiry(time.Duration):設(shè)置鎖的自動(dòng)過(guò)期時(shí)間(建議大于業(yè)務(wù)執(zhí)行時(shí)間)。
  • WithTries(int):設(shè)置最大重試次數(shù)。
  • WithRetryDelay(time.Duration):設(shè)置重試間隔。

使用示例:

mutex := rs.NewMutex("test-redsync",
    redsync.WithExpiry(30*time.Second),
    redsync.WithTries(3),
    redsync.WithRetryDelay(500*time.Millisecond),
)

看門(mén)狗

我們現(xiàn)在知道使用分布式鎖一定要設(shè)置一個(gè)過(guò)期時(shí)間了,但是這會(huì)帶來(lái)另外一個(gè)問(wèn)題:如果我們的業(yè)務(wù)代碼還沒(méi)執(zhí)行完,鎖就過(guò)期自動(dòng)釋放了,那么此時(shí)另外一個(gè)進(jìn)程成功拿到這把鎖,也來(lái)訪問(wèn)競(jìng)態(tài)資源,那分布式鎖不就失去意義了嗎?

這就引出了使用分布式鎖的另一個(gè)重要問(wèn)題,鎖自動(dòng)續(xù)期。我舉一個(gè)代碼示例,你就懂了:

package main

import (
	"context"
	"log/slog"
	"time"

	"github.com/go-redsync/redsync/v4"                  // 引入 redsync 庫(kù),用于實(shí)現(xiàn)基于 Redis 的分布式鎖
	"github.com/go-redsync/redsync/v4/redis/goredis/v9" // 引入 redsync 的 goredis 連接池
	goredislib "github.com/redis/go-redis/v9"           // 引入 go-redis 庫(kù),用于與 Redis 服務(wù)器通信
)

func main() {
	// 創(chuàng)建一個(gè) Redis 客戶端
	client := goredislib.NewClient(&goredislib.Options{
		Addr:     "localhost:36379", // Redis 服務(wù)器地址
		Password: "nightwatch",
	})

	// 使用 go-redis 客戶端創(chuàng)建一個(gè) redsync 連接池
	pool := goredis.NewPool(client)

	// 創(chuàng)建一個(gè) redsync 實(shí)例,用于管理分布式鎖
	rs := redsync.New(pool)

	// 創(chuàng)建一個(gè)名為 "test-redsync" 的互斥鎖(Mutex)
	mutex := rs.NewMutex("test-redsync", redsync.WithExpiry(5*time.Second))

	// 創(chuàng)建一個(gè)上下文(context),一般用于控制鎖的超時(shí)和取消
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 獲取鎖,如果獲取失?。ɡ珂i已被其他進(jìn)程持有),會(huì)返回錯(cuò)誤
	if err := mutex.LockContext(ctx); err != nil {
		panic(err) // 如果獲取鎖失敗,程序會(huì) panic
	}

	// 看門(mén)狗,實(shí)現(xiàn)鎖自動(dòng)續(xù)約
	stopCh := make(chan struct{})
	ticker := time.NewTicker(2 * time.Second) // 每隔 2s 續(xù)約一次
	defer ticker.Stop()
	go func() {
		for {
			select {
			case <-ticker.C:
				// 續(xù)約,延長(zhǎng)鎖的過(guò)期時(shí)間
				if ok, err := mutex.ExtendContext(ctx); !ok || err != nil {
					slog.Error("Failed to extend mutex", "err", err, "status", ok)
				} else {
					slog.Info("Successfully extend mutex")
				}
			case <-stopCh:
				slog.Info("Exiting mutex watchdog")
				return
			}
		}
	}()

	// 執(zhí)行業(yè)務(wù)邏輯
	time.Sleep(6 * time.Second)

	// 通知看門(mén)狗停止自動(dòng)續(xù)期
	stopCh <- struct{}{}

	// 釋放鎖,如果釋放失敗(例如鎖已過(guò)期或不屬于當(dāng)前進(jìn)程),會(huì)返回錯(cuò)誤
	if _, err := mutex.UnlockContext(ctx); err != nil {
		panic(err) // 如果釋放鎖失敗,程序會(huì) panic
	}
}

這個(gè)示例延續(xù)了前文中的示例代碼,你需要重點(diǎn)關(guān)注的是如下這部分邏輯:

// 看門(mén)狗,實(shí)現(xiàn)鎖自動(dòng)續(xù)約
stopCh := make(chan struct{})
ticker := time.NewTicker(2 * time.Second) // 每隔 2s 續(xù)約一次
defer ticker.Stop()
go func() {
    for {
        select {
        case <-ticker.C:
            // 續(xù)約,延長(zhǎng)鎖的過(guò)期時(shí)間
            if ok, err := mutex.ExtendContext(ctx); !ok || err != nil {
                slog.Error("Failed to extend mutex", "err", err, "status", ok)
            } else {
                slog.Info("Successfully extend mutex")
            }
        case <-stopCh:
            slog.Info("Exiting mutex watchdog")
            return
        }
    }
}()

redsync 提供了 mutex.ExtendContext(ctx) 方法可以延長(zhǎng)鎖的過(guò)期時(shí)間。假設(shè)我們申請(qǐng)的分布式鎖過(guò)期時(shí)間是 5 秒,而業(yè)務(wù)代碼執(zhí)行時(shí)間是未知的,那么我們?cè)谀玫芥i以后,可以單獨(dú)開(kāi)啟一個(gè) goroutine 來(lái)定時(shí)延長(zhǎng)鎖的過(guò)期時(shí)間,當(dāng)業(yè)務(wù)代碼執(zhí)行完成以后,主 goroutine 通過(guò) stopCh <- struct{}{} 向子 goroutine 發(fā)送停止信號(hào),那么子 goroutine 中的 <-stopCh case 就會(huì)收到通知,子 goroutine 便會(huì)退出,也就停止了鎖自動(dòng)續(xù)期。

通過(guò)為分布式鎖設(shè)置過(guò)期時(shí)間,再配合子 goroutine 自動(dòng)續(xù)期的功能,我們就能保證,持有鎖的進(jìn)程掛掉時(shí)不會(huì)影響其他進(jìn)程獲取鎖,并且還能實(shí)現(xiàn)業(yè)務(wù)執(zhí)行完成后才釋放鎖。而這個(gè)實(shí)現(xiàn)分布式鎖自動(dòng)續(xù)期的程序,我們通常把它叫做“看門(mén)狗”。

我再額外啰嗦一句,關(guān)于分布式鎖的續(xù)期時(shí)常和間隔周期的問(wèn)題,一般來(lái)說(shuō),續(xù)期的時(shí)間可以設(shè)置為等于過(guò)期時(shí)間,即鎖的過(guò)期時(shí)間設(shè)為 5 秒,那么每次也只續(xù)期 5 秒,redsync 內(nèi)部也是這么做的,至于間隔多久續(xù)期一次,這個(gè)時(shí)間肯定是要小于過(guò)期時(shí)間 5 秒的,通常設(shè)為鎖過(guò)期時(shí)間的 1/3 或 1/2 都可以。

redsync 原理

我上面講解的 redsync 用法基本上能覆蓋業(yè)務(wù)開(kāi)發(fā)中的大部分場(chǎng)景了,對(duì)于 redsync 更多的功能我就不過(guò)多介紹了,有了現(xiàn)有的知識(shí),你遇到了問(wèn)題也可以自己查閱文檔學(xué)習(xí)。 下面我想講點(diǎn)更有價(jià)值的東西,我們自己來(lái)實(shí)現(xiàn)一個(gè)微型的 Redis 分布式鎖,以此來(lái)加深你對(duì) redsync 的理解。

如何實(shí)現(xiàn)一個(gè) Redis 分布式鎖

要基于 Redis 實(shí)現(xiàn)一個(gè)最小化的分布式鎖,我們可以定義一個(gè)結(jié)構(gòu)體 MiniRedisMutex 作為鎖對(duì)象:

type MiniRedisMutex struct {
	name   string        // 會(huì)作為分布式鎖在 Redis 中的 key
	expiry time.Duration // 鎖過(guò)期時(shí)間
	conn   redis.Cmdable // Redis Client
}

它僅包含必要的字段,name 是鎖的名稱,expiry 是分布式鎖必須要有的過(guò)期時(shí)間,conn 用來(lái)存儲(chǔ) Redis 客戶端連接。

我們可以定義一個(gè)構(gòu)造函數(shù) NewMutex 來(lái)創(chuàng)建分布式鎖對(duì)象:

func NewMutex(name string, expiry time.Duration, conn redis.Cmdable) *MiniRedisMutex {
	return &MiniRedisMutex{name, expiry, conn}
}

接下來(lái)就要實(shí)現(xiàn)加鎖和解鎖這兩個(gè)功能。

加鎖方法 Lock 實(shí)現(xiàn)如下:

func (m *MiniRedisMutex) Lock(ctx context.Context, value string) (bool, error) {
	reply, err := m.conn.SetNX(ctx, m.name, value, m.expiry).Result()
	if err != nil {
		return false, err
	}
	return reply, nil
}

Lock 方法接收兩個(gè)參數(shù),ctx 用來(lái)控制取消,value 則會(huì)作為鎖的值。

Lock 方法內(nèi)部邏輯非常簡(jiǎn)單,直接調(diào)用 Redis 的 SetNX 命令來(lái)排他的設(shè)置一個(gè)鍵值對(duì),鎖名稱 name 作為 Redis 的 key,鎖的值 value 作為 Redis 的 value,并指定過(guò)期時(shí)間為 expiry,這就是分布式鎖的加鎖原理。

這里有兩個(gè)關(guān)鍵點(diǎn)需要你注意:

  • 使用 SetNX 命令:這里之所以使用 SetNX 命令而不是普通的 Set 命令,是因?yàn)榧渔i操作需要排他性。我們知道,SetNX 命令的全稱是 SET if Not eXists,即通過(guò) SetNX 命令設(shè)置鍵值對(duì)時(shí),如果 key 不存在,設(shè)置其 value,若 key 已存在,則不執(zhí)行任何操作。這剛好符合互斥性,是實(shí)現(xiàn)分布式互斥鎖的關(guān)鍵所在。
  • value 唯一性:雖然 SetNX 命令能夠?qū)崿F(xiàn)互斥,但是 Redis 的 value 還是要保證唯一性。這一點(diǎn)我們接著往下看你就明白了。

釋放鎖方法 Unlock 實(shí)現(xiàn)如下:

// 釋放鎖的 lua 腳本,保證并發(fā)安全
var deleteScript = `
	local val = redis.call("GET", KEYS[1])
	if val == ARGV[1] then
		return redis.call("DEL", KEYS[1])
	elseif val == false then
		return -1
	else
		return 0
	end
`

// Unlock 釋放鎖
func (m *MiniRedisMutex) Unlock(ctx context.Context, value string) (bool, error) {
	// 執(zhí)行 lua 腳本,Redis 會(huì)保證其并發(fā)安全
	status, err := m.conn.Eval(ctx, deleteScript, []string{m.name}, value).Result()
	if err != nil {
		return false, err
	}
	if status == int64(-1) {
		return false, ErrLockAlreadyExpired
	}
	return status != int64(0), nil
}

在釋放鎖的邏輯中,我們不是簡(jiǎn)單的將指定的 Redis 鍵值對(duì)刪除即可,而是調(diào)用 m.conn.Eval 方法執(zhí)行了一段 lua 腳本的方式來(lái)釋放鎖。

在這段 lua 腳本中,我們先是從 Redis 中獲取指定 keym.name 的鍵值對(duì),然后判斷其 value 是否等于 Unlock 方法傳入的 value 參數(shù)值,如果相等,則從 Redis 中刪除指定的鍵值對(duì),表示釋放鎖,否則什么也不做。

之所以要對(duì) value 進(jìn)行判斷,是因?yàn)槲覀円WC這把鎖是當(dāng)前進(jìn)程所持有的鎖,而不是其他進(jìn)程持有的鎖。那么以什么為依據(jù)來(lái)說(shuō)明這把鎖是當(dāng)前進(jìn)程持有的呢?這就是我們要保證 value 唯一的原因,每個(gè)進(jìn)程在加鎖的時(shí)候,需要生成一個(gè)隨機(jī)的 value 作為自己的鎖的標(biāo)識(shí),那么釋放時(shí),就可以通過(guò)這個(gè) value 來(lái)判斷是否是自己持有的鎖。而這樣做的目的,是為了避免一個(gè)進(jìn)程搶到鎖后,還在執(zhí)行業(yè)務(wù)邏輯時(shí),鎖被另外一個(gè)進(jìn)程給釋放了。

遺憾的是,這段釋放鎖的邏輯,Redis 沒(méi)有提供像 SetNX 一樣的快捷命令,所以我們只能將其放在 lua 腳本中執(zhí)行,才能保證并發(fā)安全。

至此,一個(gè)微型的 Redis 分布式鎖的核心功能咱們就講解完成了。

以下是 MiniRedisMutex 分布式鎖完整的代碼實(shí)現(xiàn):

package miniredislock

import (
	"context"
	"errors"
	"time"

	"github.com/redis/go-redis/v9"
)

var ErrLockAlreadyExpired = errors.New("miniredislock: failed to unlock, lock was already expired")

// MiniRedisMutex 一個(gè)微型的 Redis 分布式鎖
type MiniRedisMutex struct {
	name   string        // 會(huì)作為分布式鎖在 Redis 中的 key
	expiry time.Duration // 鎖過(guò)期時(shí)間
	conn   redis.Cmdable // Redis Client
}

// NewMutex 創(chuàng)建 Redis 分布式鎖
func NewMutex(name string, expiry time.Duration, conn redis.Cmdable) *MiniRedisMutex {
	return &MiniRedisMutex{name, expiry, conn}
}

// Lock 加鎖
func (m *MiniRedisMutex) Lock(ctx context.Context, value string) (bool, error) {
	reply, err := m.conn.SetNX(ctx, m.name, value, m.expiry).Result()
	if err != nil {
		return false, err
	}
	return reply, nil
}

// 釋放鎖的 lua 腳本,保證并發(fā)安全
var deleteScript = `
	local val = redis.call("GET", KEYS[1])
	if val == ARGV[1] then
		return redis.call("DEL", KEYS[1])
	elseif val == false then
		return -1
	else
		return 0
	end
`

// Unlock 釋放鎖
func (m *MiniRedisMutex) Unlock(ctx context.Context, value string) (bool, error) {
	// 執(zhí)行 lua 腳本,Redis 會(huì)保證其并發(fā)安全
	status, err := m.conn.Eval(ctx, deleteScript, []string{m.name}, value).Result()
	if err != nil {
		return false, err
	}
	if status == int64(-1) {
		return false, ErrLockAlreadyExpired
	}
	return status != int64(0), nil
}

其實(shí),這段代碼的主要邏輯,都是我從 redsync 源碼中提取出來(lái)。所以 redsync 其實(shí)也是這樣實(shí)現(xiàn)的,只不過(guò)它內(nèi)部增加了很多可靠性和邊緣場(chǎng)景等邏輯代碼,最核心的加鎖和解鎖邏輯是一樣的。

微型分布式鎖使用

下面我們來(lái)寫(xiě)一個(gè)示例程序,演示下如何使用這個(gè)微型的分布式鎖:

package main

import (
	"fmt"
	"time"

	goredislib "github.com/redis/go-redis/v9"
	"golang.org/x/net/context"

	"github.com/jianghushinian/blog-go-example/redsync/miniredislock"
)

func main() {
	// 創(chuàng)建一個(gè) Redis 客戶端
	client := goredislib.NewClient(&goredislib.Options{
		Addr:     "localhost:36379", // Redis 服務(wù)器地址
		Password: "nightwatch",
	})
	defer client.Close()

	// 創(chuàng)建一個(gè)名為 "test-miniredislock" 的互斥鎖
	mutex := miniredislock.NewMutex("test-miniredislock", 5*time.Second, client)

	ctx := context.Background()
	// 互斥鎖的值應(yīng)該是一個(gè)隨機(jī)值
	value := "random-string"

	// 獲取鎖
	_, err := mutex.Lock(ctx, value)
	if err != nil {
		panic(err)
	}

	// 執(zhí)行業(yè)務(wù)邏輯
	fmt.Println("do something...")
	time.Sleep(3 * time.Second)

	// 釋放自己持有的鎖
	_, err = mutex.Unlock(ctx, value)
	if err != nil {
		panic(err)
	}
}

這個(gè)示例的具體邏輯我就不逐行講解了,相信你一看便懂。也希望你能夠自己在本機(jī)上跑起來(lái)這段代碼,真正用一下分布式鎖,以此加深理解。

最后我再留一個(gè)作業(yè),你可以嘗試一下實(shí)現(xiàn)鎖的續(xù)期方法 Extend。

總結(jié)

分布式鎖可以確保分布式系統(tǒng)中并發(fā)安全的訪問(wèn)競(jìng)態(tài)資源,redsync 作為 Go 中最流行的 Redis 分布式鎖方案,非常值得我們學(xué)習(xí)和使用。

redsync 的用法非常簡(jiǎn)單,加鎖和解鎖操作與 sync.Mutex 也非常類(lèi)似,沒(méi)有太多的學(xué)習(xí)成本。不過(guò),為了避免持有鎖的進(jìn)程掛掉時(shí),其他進(jìn)程還有機(jī)會(huì)獲取鎖,我們需要實(shí)現(xiàn)看門(mén)狗的功能。

到此這篇關(guān)于Go語(yǔ)言如何使用分布式鎖解決并發(fā)問(wèn)題的文章就介紹到這了,更多相關(guān)Go分布式鎖解決并發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang接收post和get請(qǐng)求參數(shù)處理

    golang接收post和get請(qǐng)求參數(shù)處理

    本文主要介紹了golang接收post和get請(qǐng)求參數(shù)處理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • 一文詳解Go語(yǔ)言中切片的底層原理

    一文詳解Go語(yǔ)言中切片的底層原理

    在Go語(yǔ)言中,切片作為一種引用類(lèi)型數(shù)據(jù),相對(duì)數(shù)組而言是一種動(dòng)態(tài)長(zhǎng)度的數(shù)據(jù)類(lèi)型,使用的場(chǎng)景也是非常多,所以本文主要來(lái)和大家聊聊切片的底層原理,需要的可以參考一下
    2023-06-06
  • 關(guān)于Golang中range指針數(shù)據(jù)的坑詳解

    關(guān)于Golang中range指針數(shù)據(jù)的坑詳解

    這篇文章主要給大家介紹了關(guān)于Golang中range指針數(shù)據(jù)的坑的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • Go語(yǔ)言使用Etcd實(shí)現(xiàn)分布式鎖

    Go語(yǔ)言使用Etcd實(shí)現(xiàn)分布式鎖

    etcd是近幾年比較火熱的一個(gè)開(kāi)源的、分布式的鍵值對(duì)數(shù)據(jù)存儲(chǔ)系統(tǒng),本文將介紹如何利用Etcd實(shí)現(xiàn)分布式鎖,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-05-05
  • Go語(yǔ)言文件操作的方法

    Go語(yǔ)言文件操作的方法

    這篇文章主要介紹了Go語(yǔ)言文件操作的方法,涉及文件的讀寫(xiě)及關(guān)閉等操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • golang中reflect包的常用方法

    golang中reflect包的常用方法

    Go反射reflect包提供類(lèi)型和值方法,用于獲取類(lèi)型信息、訪問(wèn)字段、調(diào)用方法等,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-07-07
  • Go設(shè)計(jì)模式之觀察者模式圖解

    Go設(shè)計(jì)模式之觀察者模式圖解

    觀察者模式是一種行為設(shè)計(jì)模式, 允許你定義一種訂閱機(jī)制, 可在對(duì)象事件發(fā)生時(shí)通知多個(gè) “觀察” 該對(duì)象的其他對(duì)象,下面這篇文章主要給大家介紹了關(guān)于圖解Go觀察者模式的相關(guān)資料,需要的朋友可以參考下
    2023-07-07
  • 使用Go語(yǔ)言創(chuàng)建WebSocket服務(wù)的實(shí)現(xiàn)示例

    使用Go語(yǔ)言創(chuàng)建WebSocket服務(wù)的實(shí)現(xiàn)示例

    這篇文章主要介紹了使用Go語(yǔ)言創(chuàng)建WebSocket服務(wù)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Golang 實(shí)現(xiàn)超大文件讀取的兩種方法

    Golang 實(shí)現(xiàn)超大文件讀取的兩種方法

    這篇文章主要介紹了Golang 實(shí)現(xiàn)超大文件讀取的兩種方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • 使用go語(yǔ)言解析xml的實(shí)現(xiàn)方法(必看篇)

    使用go語(yǔ)言解析xml的實(shí)現(xiàn)方法(必看篇)

    下面小編就為大家?guī)?lái)一篇使用go語(yǔ)言解析xml的實(shí)現(xiàn)方法(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06

最新評(píng)論

商水县| 衡南县| 平顶山市| 罗定市| 永仁县| 淮阳县| 英德市| 阳朔县| 利辛县| 纳雍县| 通化市| 云霄县| 嘉荫县| 黎平县| 辽宁省| 利辛县| 界首市| 正定县| 博罗县| 东阳市| 临江市| 安顺市| 乌拉特中旗| 六盘水市| 滨海县| 原平市| 江津市| 全椒县| 贺兰县| 江山市| 游戏| 岑溪市| 汕尾市| 罗江县| 东莞市| 平湖市| 久治县| 翁牛特旗| 谢通门县| 三河市| 安阳县|