golang 实现Redis分布式锁

package main

import (
"github.com/gomodule/redigo/redis"
"time"
"fmt"
"errors"
)

type Redis struct {
pool *redis.Pool
key string
value string
timeout int
}

var redisPool *Redis

func initRedis() {
redisPool = new(Redis)
redisPool.pool = &redis.Pool{
MaxIdle: 256, //池中最大空闲连接数。
MaxActive: 0, //给定时间池分配的最大连接数。 如果为零,则池中的连接数没有限制。
IdleTimeout: time.Duration(120), //在此期间保持空闲后关闭连接。 如果该值为零,则不关闭空闲连接。 应用程序应将超时设置为小于服务器超时的值。
Dial: func() (redis.Conn, error) { //拨号是应用程序提供的用于创建和配置连接的功能。 从Dial返回的连接不得处于特殊状态(已订阅pubsub通道,事务已开始,...)。
return redis.Dial(
"tcp",
"127.0.0.1:7240",
redis.DialReadTimeout(time.Duration(1000)*time.Millisecond), //DialReadTimeout指定读取单个命令答复的超时。
redis.DialWriteTimeout(time.Duration(1000)*time.Millisecond), //DialWriteTimeout制定写入单个命令答复的超时时间
redis.DialConnectTimeout(time.Duration(1000)*time.Millisecond), //DialConnectTimeout指定在未指定DialNetDial选项时连接到Redis服务器的超时时间。
redis.DialDatabase(0),
//red.DialPassword(""),
)
},
}
}

func (r *Redis) Lock() (ok bool, err error) {
c := r.pool.Get()
defer c.Close()
//设置锁key-value和过期时间
_, err = redis.String(c.Do("SET", r.key, r.value, "EX", r.timeout, "NX"))
if err != nil {
if err == redis.ErrNil {
return false, nil
}
return false, err
}
return true, nil
}

func (r *Redis) Unlock(value string) (err error) {
c := r.pool.Get()
defer c.Close()
//获取锁value
setValue, err := redis.String(c.Do("GET", r.key))
if err != nil {
return
}
//判断锁是否属于该释放锁的线程
if setValue != value {
err = errors.New("非法用户,无法释放该锁")
return
}
//属于该用户,直接删除该key
_, err = c.Do("DEL", r.key)
return
}

//延期,应该判断value后再延期

func (r *Redis) AddTimeout() (ok bool, err error) {
c := r.pool.Get()
defer c.Close()

//获取key的剩余有效时间 当key不存在时返回-2 当未设置过期时间的返回-1
ttl_time, err := redis.Int64(c.Do("TTL", r.key))
if err != nil {
fmt.Println("redis get fail:", err)
return
}
if ttl_time > 0 {
_, err = redis.String(c.Do("SET", r.key, r.value, "EX", r.timeout))
if err == redis.ErrNil {
return false, nil
}
if err != nil {
return false, err
}
}
return true, nil
}

func main() {
//初始化redis连接池
initRedis()

r := &Redis{
key: "hello",
value: "lock",
timeout: 10,
pool: redisPool.pool,
}
ok, _ := r.Lock()
if ok {
fmt.Println("加锁成功:")

//创建协程,定时延期锁的过期时间
go func(r *Redis) {
for {
select {
case <-time.After(time.Duration(r.timeout-1) * time.Second):
r.AddTimeout()
}
}
}(r)
err := r.Unlock("haha")
if err != nil {
fmt.Println("释放锁失败:", err)
} else {
fmt.Println("成功释放锁:")
}
} else {
fmt.Println("加锁失败:")
}

//阻塞进程,避免退出
select {}
}

猜你喜欢

转载自www.cnblogs.com/yang-she/p/12660888.html
今日推荐