很多并发情况下需要锁的加入,例如商品秒杀场景。本文介绍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)
}
执行结果
获得锁头的协程完成了操作
未所得的等待,直到超时