go+redis模拟实现分布式锁

参考这篇文章# Redis分布式锁的正确实现方式 实现的分布式锁
整个过程可以分为四步:
+先用SETNX设置key。key是唯一的,统一时刻只有一个客户端的setnx指令能成功,满足锁的唯一性。如果成功,说明没有人持有锁,否则锁被占有,则获取锁的释放时间,然后进入睡眠。睡眠时间为一个任意的值,小于锁的TTL。

  • 用EXPIRE设置key。expire是为了防止客户端崩溃锁变成死锁。SETNX和EXPIRE两个指令应该是原子的,go下面没法实现。需要修改redis支持。
  • 获取到锁后干活
  • 释放锁
package main

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

const dist_lock = "distlock"
func main() {
    redis_conn, err := redis.Dial("tcp", "127.0.0.1:6379", redis.DialPassword("hdiot"))
    if err != nil {
        fmt.Println(err)
        return
    }
    defer redis_conn.Close()

    pidstr := strconv.Itoa(os.Getpid())
    for {
        fmt.Println("try lock")
        _, err := redis_conn.Do("SETNX", dist_lock, pidstr)
        if err != nil {
            fmt.Println(err)

            ttl, err := redis.Int(redis_conn.Do("TTL", dist_lock))
            if err != nil {
                fmt.Println(err)
            }
            time.Sleep(time.Duration(ttl/2) * time.Second)  
        } else {
            // we get lock
            break;
        }
    }

    _, err = redis_conn.Do("EXPIRE", dist_lock, 10)
    if err != nil {
        fmt.Println(err)
        return      
    }

    // do something
    fmt.Println("get lock")

    // release lock
    pid_ret, err := redis.String(redis_conn.Do("GET", dist_lock))
    if err != nil {
        fmt.Println(err)
        return
    }

    pid, _ := strconv.Atoi(pid_ret)
    if(pid != os.Getpid()) {
        fmt.Println("not my lock")
        return
    }

    _, err = redis_conn.Do("Del", dist_lock)
    if err != nil {
        fmt.Println(err)
        return      
    }
    fmt.Println("release lock ok")
}

你可能感兴趣的:(go+redis模拟实现分布式锁)