goframe通用系列-利用redis实现锁

510 阅读1分钟

很多并发情况下需要锁的加入,例如商品秒杀场景。本文介绍goframe + redis实现简单的锁

锁代码


package syslock

import (
	"app/app/common/service/lib/nosql/easyredis"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/syyongx/php2go"
)

var EasyLockService = EasyLockServiceStruct{}

type EasyLockServiceStruct struct {
    Key     string
    Value   string
    TimeOut int
    RedisHandle *easyredis.EasyRedisServiceStruct
}

//初始化锁
func New(key string) *EasyLockServiceStruct {
    return &EasyLockServiceStruct{
        Key:     key,
        Value:   php2go.Uniqid("sys_lock_") + gconv.String(php2go.Rand(100, 999)),
        TimeOut: 10,
        RedisHandle: easyredis.New(),
    }
}

//带参数初始化
func NewWithParams(key, value string, timeOut int) *EasyLockServiceStruct {
    return &EasyLockServiceStruct{
        Key:     key,
        Value:   value,
        TimeOut: timeOut,
        RedisHandle: easyredis.New(),
    }
}

//上锁
func (this *EasyLockServiceStruct) Lock() bool {
    return this.RedisHandle.SetNx(this.Key, this.Value, this.TimeOut)
}

//解锁
func (this *EasyLockServiceStruct) UnLock() bool {
	const unlockScript = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"
	result, _ := this.RedisHandle.Handle().Do(nil, "EVAL", unlockScript, 1, this.Key, this.Value)
	return result.Bool()
}

执行,(未获得锁的等待,超时后返回网络拥堵)


var testmap = g.Map{}
//模拟并发写map
func TestLock(i int) {
	key := "lock_test_key"
	startTime := php2go.Time()
	l := syslock.New(key)
	for {
            time.Sleep(5 * time.Millisecond)
            if l.Lock() == true {
                    break
            }
            currTime := php2go.Time()
            if currTime-startTime >= 8 {
                    g.Dump("网络拥堵")
            }
	}
	defer l.UnLock()
	testmap[gconv.String(i)] = "完成操作"
	//模拟业务操作需要30毫秒
	time.Sleep(30 * time.Millisecond)
	g.Dump("线程" + gconv.String(i) + "获得锁头")
}

调用

func main() {
	//模拟1000个请求
	for i := 0; i < 1000; i++ {
            go TestLock(i)
	}
	php2go.Sleep(10)
	g.Dump(testmap)
}

执行结果

image.png 获得锁头的协程完成了操作 未所得的等待,直到超时