Jika anda biasa dengan arahan Redis, anda boleh segera memikirkan untuk menggunakan set Redis jika tidak wujud operasi untuk melaksanakannya, dan ia kini standard Pelaksanaannya ialah siri arahan SET resource_name my_random_value NX PX 30000, di mana:
resource_name mewakili sumber yang akan dikunci
NX mewakili jika ia tidak wujud Kemudian tetapkan
PX 30000 untuk menunjukkan masa tamat tempoh ialah 30000 milisaat, iaitu 30 saat
my_random_value mestilah unik di kalangan semua pelanggan , semua pemeroleh (pesaing) kunci yang sama tidak boleh mempunyai nilai yang sama.
Nilai nilai mestilah nombor rawak terutamanya untuk melepaskan kunci dengan lebih selamat Apabila melepaskan kunci, gunakan skrip untuk memberitahu Redis: hanya kunci yang wujud dan nilai yang disimpan sama seperti nilai yang saya nyatakan Barulah saya boleh diberitahu bahawa pemadaman itu berjaya. Ini boleh dicapai melalui skrip Lua berikut:
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
Contohnya: Klien A memperoleh kunci sumber, tetapi disekat dengan serta-merta oleh operasi lain Apabila Klien A mahu melepaskan kunci selepas menjalankan operasi lain, ia bertukar bahawa Kunci telah tamat masa dan telah dikeluarkan secara automatik oleh Redis, dan dalam tempoh ini kunci sumber telah diperoleh semula oleh klien B.
Skrip Lua digunakan kerana penghakiman dan pemadaman adalah dua operasi, jadi ada kemungkinan A akan melepaskan kunci secara automatik selepas ia tamat tempoh sebaik sahaja ia menilainya, dan kemudian B memperoleh kunci itu, dan kemudian A memanggil Del, menyebabkan B ke Kunci dilepaskan.
package main import ( "context" "errors" "fmt" "github.com/brianvoe/gofakeit/v6" "github.com/go-redis/redis/v8" "sync" "time" ) var client *redis.Client const unlockScript = ` if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end` func lottery(ctx context.Context) error { // 加锁 myRandomValue := gofakeit.UUID() resourceName := "resource_name" ok, err := client.SetNX(ctx, resourceName, myRandomValue, time.Second*30).Result() if err != nil { return err } if !ok { return errors.New("系统繁忙,请重试") } // 解锁 defer func() { script := redis.NewScript(unlockScript) script.Run(ctx, client, []string{resourceName}, myRandomValue) }() // 业务处理 time.Sleep(time.Second) return nil } func main() { client = redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", }) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() ctx, _ := context.WithTimeout(context.Background(), time.Second*3) err := lottery(ctx) if err != nil { fmt.Println(err) } }() go func() { defer wg.Done() ctx, _ := context.WithTimeout(context.Background(), time.Second*3) err := lottery(ctx) if err != nil { fmt.Println(err) } }() wg.Wait() }
Mari kita lihat dahulu fungsi loteri(), yang menyerupai operasi loteri Apabila memasuki fungsi, mula-mula gunakan SET resource_name my_random_value NX PX 30000 untuk mengunci, di sini gunakan UUID sebagai nilai Rawak Jika operasi gagal, ia akan kembali secara langsung dan membolehkan pengguna mencuba lagi Jika logik buka kunci berjaya dilaksanakan dalam penangguhan, logik buka kunci adalah untuk melaksanakan skrip Lua yang disebutkan di atas dan kemudian melakukan pemprosesan perniagaan.
Kami melaksanakan dua goroutine dalam fungsi main() untuk memanggil fungsi lottery() secara serentak Salah satu operasi akan gagal secara langsung kerana kunci tidak boleh diperolehi.
Jana nilai rawak
Gunakan SET resource_name my_random_value NX PX 30000 untuk mengunci
<🎜 🎜>Contoh Tambah dan Buka Kunci
package main import ( "context" "errors" "fmt" "github.com/brianvoe/gofakeit/v6" "github.com/go-redis/redis/v8" "sync" "time" ) var clients []*redis.Client const unlockScript = ` if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end` func lottery(ctx context.Context) error { // 加锁 myRandomValue := gofakeit.UUID() resourceName := "resource_name" var wg sync.WaitGroup wg.Add(len(clients)) // 这里主要是确保不要加锁太久,这样会导致业务处理的时间变少 lockCtx, _ := context.WithTimeout(ctx, time.Millisecond*5) // 成功获得锁的Redis实例的客户端 successClients := make(chan *redis.Client, len(clients)) for _, client := range clients { go func(client *redis.Client) { defer wg.Done() ok, err := client.SetNX(lockCtx, resourceName, myRandomValue, time.Second*30).Result() if err != nil { return } if !ok { return } successClients <- client }(client) } wg.Wait() // 等待所有获取锁操作完成 close(successClients) // 解锁,不管加锁是否成功,最后都要把已经获得的锁给释放掉 defer func() { script := redis.NewScript(unlockScript) for client := range successClients { go func(client *redis.Client) { script.Run(ctx, client, []string{resourceName}, myRandomValue) }(client) } }() // 如果成功加锁得客户端少于客户端数量的一半+1,表示加锁失败 if len(successClients) < len(clients)/2+1 { return errors.New("系统繁忙,请重试") } // 业务处理 time.Sleep(time.Second) return nil } func main() { clients = append(clients, redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 0, }), redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 1, }), redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 2, }), redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 3, }), redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 4, })) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() ctx, _ := context.WithTimeout(context.Background(), time.Second*3) err := lottery(ctx) if err != nil { fmt.Println(err) } }() go func() { defer wg.Done() ctx, _ := context.WithTimeout(context.Background(), time.Second*3) err := lottery(ctx) if err != nil { fmt.Println(err) } }() wg.Wait() time.Sleep(time.Second) }
Kemudian tambah tangguh untuk melepaskan logik kunci Logik pelepas kunci adalah sangat mudah, cuma lepaskan kunci yang berjaya diperolehi.
Akhir sekali, nilai sama ada bilangan kunci yang berjaya diperoleh adalah lebih daripada separuh Jika lebih daripada separuh kunci tidak diperoleh, ini bermakna penguncian gagal.
Jika penguncian berjaya, langkah seterusnya ialah melakukan pemprosesan perniagaan.
Ringkasan
SET resource_name my_random_value NX PX 30000
Atas ialah kandungan terperinci Cara melaksanakan kunci teragih dalam Go digabungkan dengan Redis. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!