在分布式场景中,也需要“抢占”的逻辑,可以用 Redis 的setnx
实现:
package main
import (
"github.com/go-redis/redis"
)
func setnx() {
client := redis.NewClient(&redis.Options{})
var lockKey = "counter_lock"
var counterKey = "counter"
// lock
resp := client.SetNX(lockKey, 1, time.Second*5)
lockStatus, err := resp.Result()
if err != nil || !lockStatus {
println("lock failed")
return
}
// counter++
getResp := client.Get(counterKey)
cntValue, err := getResp.Int64()
if err == nil || err == redis.Nil {
cntValue++
resp := client.Set(counterKey, cntValue, 0)
_, err := resp.Result()
if err != nil {
println(err)
}
}
println("current counter is ", cntValue)
// unlock
delResp := client.Del(lockKey)
unlockStatus, err := delResp.Result()
if err == nil && unlockStatus > 0 {
println("unlock success")
} else {
println("unlock failed", err)
}
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
setnx()
}()
}
wg.Wait()
}
运行结果:
➜ go run main.go
lock failed
lock failed
lock failed
lock failed
lock failed
lock failed
lock failed
lock failed
current counter is 34
lock failed
unlock success
通过代码和执行结果可以看到,远程调用setnx
运行流程上和单机的 trolock
非常相似,如果获取锁失败,那么相关的任务逻辑就不会继续向后执行。
setnx很适合高并发场景下用来争抢一些“唯一”的资源。比如交易摄合系统中卖家发起订单,多个买家会对其进行并发争抢。这种场景我们没有办法依赖具体的时间来判断先后,因为不同设备的时间不能保证使用的是统一的时间,也就不能保证时序。
所以,我们需要依赖于这些请求到达redis
节点的顺序来做正确的抢锁操作。
如果用户的网络环境比较差,是必然抢不到的。
最后编辑: kuteng 文档更新时间: 2022-03-22 19:29 作者:kuteng