golang 利用redis实现分布式锁

redis知识准备

redis SETNX命令
SETNX 如果key不存在则创建,并返回true
如果key存在则不操作,并返回false

redis TTL命令
如果对key设置了过期时间,则TTL命令返回key到过期时所剩余的时间
如果key没有设置过期时间,则TTL命令返回-1

代码
package main

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

type redisClient redis.Client

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

func connRedisSingle(addr, password string) *redis.Client {
    conf := redis.Options{
        Addr:     addr,
        Password: password,
    }
    return redis.NewClient(&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) getLock() (error, string) {
    ret := r.Get("hello")
    if err := ret.Err(); err != nil {
        fmt.Println("get lock error: ", err)
        return err, ""
    }
    rt, _ := ret.Bytes()
    return nil, string(rt)
}

// 获取锁的过期剩余时间
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 {}
}

发布了14 篇原创文章 · 获赞 1 · 访问量 2914

猜你喜欢

转载自blog.csdn.net/weixin_42450836/article/details/105265782