golang使用redis分布式锁 [2020年更新]

我还记得上一篇文章golang使用redis分布式锁还是18年写的,但是对于现在来说已经不适用,所以今天我更新了go使用redis分布式锁,对想用redis分布式锁的人一个简单的讲解,并快速用于实践。

  1. 首先,最需要的还是redis的go客户端 github.com/gomodule/redigo  已经不是之前的那个路径了。
  2. 然后下载 github.com/go-redsync/redsync 这个库,他就是实现分布式锁的核心。
  3. 当然你也可以自己实现,但是我今天只说这个库的用法。
// 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  

你可能感兴趣的:(GO语言,Redis)