golang 利用redis实现分布式锁
发布日期:2021-05-10 23:13:06 浏览次数:25 分类:精选文章

本文共 3168 字,大约阅读时间需要 10 分钟。

Redis知识准备

Redis SETNX命令

SETNX命令用于在Redis中设置或修改字符串值。如果key不存在,则创建key,并将值设为指定字符串,返回true。如果key已存在,则不修改值,返回false。

Redis TTL命令

TTL命令用于获取key的过期时间。如果key设置了过期时间,则返回key到过期时所剩余的时间。如果key未设置过期时间,则返回-1。

代码示例

Redis连接配置

package main
import (
"fmt"
"github.com/go-redis/redis"
"time"
)

单节点Redis客户端连接

func connRedisSingle(addr, password string) *redis.Client {
conf := redis.Options{
Addr: addr,
Password: password,
}
return redis.NewClient(&conf)
}

Redis集群客户端连接

func connRedisCluster(address []string, password string) *redis.ClusterClient {
conf := redis.ClusterOptions{
Addrs: address,
Password: password,
}
return redis.NewClusterClient(&conf)
}

锁的实现

加锁逻辑

func (r *redisClient) lock(value string) (error, bool) {
ret := r.SetNX("hello", value, time.Second*10)
if err := ret.Err(); err != nil {
fmt.Printf("set value %s error: %v\n", value, err)
return err, false
}
return nil, ret.Val()
}

解锁逻辑

func (r *redisClient) unlock() bool {
ret := r.Del("hello")
if err := ret.Err(); err != nil {
fmt.Println("unlock error: ", err)
return false
}
return true
}

优化锁获取逻辑

func (r *redisClient) retryLock() bool {
ok := false
for !ok {
err, t := r.getTTL()
if err != nil {
return false
}
if t > 0 {
fmt.Printf("锁被抢占, %f 秒后重试...\n", (t / 10).Seconds())
time.Sleep(t / 10)
}
err, ok = r.lock("Jan")
if err != nil {
return false
}
}
return ok
}

获取锁的过期时间

func (r *redisClient) getTTL() (error, time.Duration) {
ret := r.TTL("hello")
if err := ret.Err(); err != nil {
fmt.Println("get TTL error: ", err)
return err, 0
}
return nil, ret.Val()
}

多线程锁获取逻辑

func (r *redisClient) threadLock(threadId string) {
for {
err, _ := r.getLock()
if err != nil && err.Error() == "redis: nil" {
fmt.Printf("线程 %s 开始加锁\n", threadId)
err, ok := r.lock("Jan")
if err != nil {
return
}
if !ok {
if !r.retryLock() {
fmt.Printf("线程 %s 加锁失败\n", threadId)
return
}
}
fmt.Printf("线程 %s 已加锁\n", threadId)
time.Sleep(5 * time.Second)
r.unlock()
fmt.Printf("线程 %s 已释放锁\n", threadId)
return
} else if err != nil {
return
}
err, t := r.getTTL()
if err != nil {
return
}
if t > 0 {
fmt.Printf("线程 %s 锁被占用, %f 秒后重试\n", threadId, (t/10).Seconds())
time.Sleep(t/10)
}
}
}

运行主程序

func main() {
var r redisClient
address := "192.168.1.151:6379"
cl := connRedisSingle(address, "")
defer cl.Close()
r = redisClient(*cl)
// 线程1获取锁
go r.threadLock("1")
//time.Sleep(10 * time.Millisecond)
// 线程2获取锁
go r.threadLock("2")
select {}
}

Redis锁实现总结

通过上述代码示例,可以实现一个基于Redis的锁机制。代码中使用了SETNX命令来尝试加锁,如果失败则通过获取锁的过期时间和重试机制来重新获取锁。在多线程环境下,通过检查锁的状态和过期时间,实现了锁的可重入和优化。

上一篇:kafka、zookeeper配置sasl认证
下一篇:golang json浅析

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2025年04月24日 11时43分01秒