以下是一个基于Redis WATCH命令的分布式锁实现示例:
func acquireLock(conn redis.Conn, lockKey string, timeout int) (string, error) {
for {
// 生成随机值作为锁值
lockValue := strconv.Itoa(rand.Int())
// 设置锁值,并设置过期时间,避免死锁
result, err := conn.Do("SET", lockKey, lockValue, "NX", "EX", timeout)
if err != nil {
return "", err
}
// 锁设置成功,返回锁值
if result == "OK" {
return lockValue, nil
}
// 获取锁的超时时间
ttl, err := redis.Int(conn.Do("TTL", lockKey))
if err != nil {
return "", err
}
// 如果锁已经过期,可以尝试重新设置锁值
if ttl < 0 {
continue
}
// 设置锁值失败,等待一段时间后重试
time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond)
}
}
func releaseLock(conn redis.Conn, lockKey string, lockValue string) error {
// 使用WATCH命令监视锁键,避免并发修改锁键
err := conn.Send("WATCH", lockKey)
if err != nil {
return err
}
// 检查锁值是否匹配,如果匹配,则删除锁键
value, err := redis.String(conn.Do("GET", lockKey))
if err != nil && err != redis.ErrNil {
return err
}
if value == lockValue {
_, err := conn.Do("DEL", lockKey)
if err != nil {
return err
}
}
// 解除监视,释放连接
conn.Do("UNWATCH")
conn.Close()
return nil
}
在以上示例中,我们首先使用SET命令尝试设置锁键,如果设置成功,则返回锁值。如果锁键已经存在,则使用TTL命令获取锁的过期时间,并在锁已经过期的情况下尝试重新设置锁键。
在释放锁时,我们使用WATCH命令监视锁键,避免并发修改锁键。我们首先读取锁键的值,并检查它是否匹配锁值。如果匹配,则使用DEL命令删除锁键。最后,我们使用UNWATCH命令解除监视,并释放连接。
调用方式
// 获取锁,锁键为"my_lock",锁的过期时间为5秒
lockKey := "my_lock"
lockValue, err := acquireLock(pool.Get(), lockKey, 5)
if err != nil {
fmt.Println(err)
return
}
defer releaseLock(pool.Get(), lockKey, lockValue)
// TODO: 获得锁后的操作