我还记得上一篇文章golang使用redis分布式锁还是18年写的,但是对于现在来说已经不适用,所以今天我更新了go使用redis分布式锁,对想用redis分布式锁的人一个简单的讲解,并快速用于实践。
// A Mutex is a distributed mutual exclusion lock.
// 这个锁是一个分布式互斥锁
type Mutex struct {
name string //设置在redis中的 key值
expiry time.Duration //key的到期时间
tries int //重试的次数
delayFunc DelayFunc //重试的间隔时间
factor float64
quorum int
genValueFunc func() (string, error) //获取设置key的值,默认是随机的,可以自己配置
value string //获取后确定的值
until time.Time
pools []Pool
}
上面就是核心结构体,注释我也加上了,下面来看如何创建一个:
package main
import (
"github.com/go-redsync/redsync"
"github.com/gomodule/redigo/redis"
)
//获取redis连接池
func newPool() *redis.Pool {
return &redis.Pool{
MaxIdle: 3,
IdleTimeout: time.Duration(24) * time.Second,
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", "192.168.7.51:6379")
if err != nil {
panic(err.Error())
return nil, err
}
return c, err
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
if err != nil {
return err
}
return err
},
}
}
func main(){
pool := newPool()
//返回 *Redsync
r := redsync.New([]redsync.Pool{pool})
//配置redis锁,我这里设置了 过期时间和 重试的间隔时间
mutex := r.NewMutex("test-mutex", redsync.SetExpiry(time.Duration(2)*time.Second),
redsync.SetRetryDelay(time.Duration(10)*time.Millisecond))
}
这个互斥锁也配置好了,有一个地方需要注意:
NewMutex 方法的时候传入的名字就是存入redis的key,所以在分布式上,这个name 需要一样,才会有效果;
如果不一样,那么他们都是可以访问的。
接下来就是怎么去使用它了,完整代码:
package main
import (
"fmt"
"github.com/go-redsync/redsync"
"github.com/gomodule/redigo/redis"
"sync"
"testing"
"time"
)
func TestRedis(t *testing.T) {
curtime := time.Now().UnixNano()
//单个测试
/*pool := newPool()
r := redsync.New([]redsync.Pool{pool})
//配置redis锁
mutex := r.NewMutex("test-mutex", redsync.SetExpiry(time.Duration(2)*time.Second),
redsync.SetRetryDelay(time.Duration(10)*time.Millisecond))
//获取锁
if err := mutex.Lock(); err != nil {
t.Fatalf("Expected err == nil, got %q", err)
return
}
fmt.Println("add lock ....")
str, _ := redis.String(DoRedisCmdByConn(pool, "GET", "name"))
fmt.Println("before name is : ", str)
//进行写操作
DoRedisCmdByConn(pool, "SET", "name", "ywanbing")
str, _ = redis.String(DoRedisCmdByConn(pool, "GET", "name"))
fmt.Println("after name is : ", str)
//释放锁
mutex.Unlock()
fmt.Println("del lock ....")*/
//并发访问
wg := new(sync.WaitGroup)
for i := 0; i < 10; i++ {
wg.Add(1)
//这里模拟多个客户端获取redis的值
go func(i int, wg *sync.WaitGroup) {
//多个同时访问
pool := newPool()
r := redsync.New([]redsync.Pool{pool})
//配置redis锁
mutex := r.NewMutex("test-mutex", redsync.SetExpiry(time.Duration(2)*time.Second),
redsync.SetRetryDelay(time.Duration(10)*time.Millisecond))
//获取锁
if err := mutex.Lock(); err != nil {
t.Fatalf("Expected err == nil, got %q", err)
return
}
//释放锁
defer mutex.Unlock()
fmt.Println(i, "add lock ....")
str, _ := redis.String(DoRedisCmdByConn(pool, "GET", "name"))
fmt.Println("before name is : ", str)
//进行写操作
DoRedisCmdByConn(pool, "SET", "name", fmt.Sprintf("name%v", i))
str, _ = redis.String(DoRedisCmdByConn(pool, "GET", "name"))
fmt.Println("after name is : ", str)
fmt.Println(i, "del lock ....")
wg.Done()
}(i, wg)
}
wg.Wait()
fmt.Println(time.Now().UnixNano() - curtime)
}
//redis命令执行函数
func DoRedisCmdByConn(conn *redis.Pool, commandName string, args ...interface{}) (interface{}, error) {
redisConn := conn.Get()
defer redisConn.Close()
//检查与redis的连接
return redisConn.Do(commandName, args...)
}
//获取redis连接池
func newPool() *redis.Pool {
return &redis.Pool{
MaxIdle: 3,
IdleTimeout: time.Duration(24) * time.Second,
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", "192.168.7.51:6379")
if err != nil {
panic(err.Error())
return nil, err
}
return c, err
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
if err != nil {
return err
}
return err
},
}
}
只要看懂了它是如何实现,后面使用 和 sync 的 mux 使用很类似。
对于需要源码和持续学习的可以关注:GitHub仓库 ,欢迎 star