「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」
分布式系统中,共享资源常常需要安全被访问和处理。这个时候,就需要分布式锁,保障有序访问共享资源。
分布式锁的原则:
- 互斥性。 在任何时刻,保证只有一个客户端持有锁。
- 不能出现死锁。 如果在一个客户端持有锁的期间,这个客户端崩溃了,也要保证后续的其他客户端可以上锁。
- 保证上锁和解锁都是同一个客户端
基于 redis 实现分布式锁
- 当 key 存在时, 设置失败,可以保证互斥
- 设置了超时时间,避免死锁
- 当前实例程序加锁不并发冲突
基于以上,可以写出如下 Redis 分布式锁代码:
import (
"context"
"sync"
"time"
)
var mutex sync.Mutex
var rdb *redis.Client
func NewRedis() {
rdb = redis.NewClient(&redis.Options{
Addr: setting.Redis.Ip + ":" + setting.Redis.Port,
Password: "", // no password set
DB: 0, // use default DB
})
}
func Lock(ctx context.Context, key string) bool {
mutex.Lock()
defer mutex.Unlock()
if rdb == nil {
logs.CtxError(ctx, "redis client nil")
return false
}
bool, err := rdb.SetNX(key, 1, 30*time.Minute).Result()
if err != nil {
logs.CtxError(ctx, " redis setnx err:%v", err.Error())
return false
}
return bool
}
func UnLock(ctx context.Context, key string) int64 {
nums, err := rdb.Del(key).Result()
if err != nil {
logs.CtxError(ctx, "err %v", err)
return 0
}
return nums
}
上面的代码有没有问题呢, 分布式锁的还有第二个写法。
func AcquireLock(lock_key string, lock_value string, timeout uint32) bool {
// SET lock_key lock_value NX PX ttl_with_seconds
// return is_success
}
func ReleaseLock(lock_key string, lock_value string) bool {
// 在释放锁的时候加入乐观锁校验, 并通过lua脚本保证原子性
// return_val = eval (
// if redis.call("get",lock_key) == lock_value then
// return redis.call("del",lock_key)
// else
// return 0
// end
// )
return return_val!=0
}
func Process(lock_key string, lock_value string, timeout uint32) {
if AcquireLock(lock_key, lock_value, timeout) {
// 无论业务逻辑执行是否成功, 一定释放锁
defer func() {
release_success := ReleaseLock(lock_key)
if !release_success {
// 如果锁释放失败, 说明锁超时, 其他人已经获取了锁, 需要根据业务决定是否rollback刚刚的操作
// maybe rollback??
}
}()
// do something
// maybe process over lock TTL
}
}