package worker import ( "context" "errors" "goRedisDLM/engine" "goRedisDLM/redisLock" "log" "math/rand" "strconv" "time" "github.com/go-redis/redis/v8" "github.com/google/uuid" ) type Inventory struct { Client *redis.Client } func (i *Inventory) Work(request engine.Request, response *engine.Response) error { value := uuid.New() //log.Printf("uuid: %s, lock key: %s\n", value.String(), request.Dlock) ctx := context.Background() lock := redisLock.CreateRedisLock(i.Client, ctx, request.Dlock, value.String(), 100) lock.Lock() time.Sleep(time.Duration(rand.Int31n(100)) * time.Millisecond) get := i.Client.Get(ctx, "inventory") log.Printf("%s\n", get) inventory, err := strconv.Atoi(get.Val()) if err != nil { goto Error } inventory = inventory - 1 if inventory >= 0 { //这里应该使用lua脚本,再检测一下当前是否获取到锁,获取到之后再进行扣减。类似双检加锁的思想,因为此时可能会续期失败。 //i.Client.Set(ctx, "inventory", fmt.Sprintf("%d", inventory), redis.KeepTTL) script := `if redis.call('hexists',KEYS[1],ARGV[1]) == 1 then return redis.call('set',KEYS[2],ARGV[2]) else return 0 end` val := i.Client.Eval(ctx, script, []string{request.Dlock, "inventory"}, value.String(), inventory).Val() if res, ok := val.(string); ok && res == "OK" { } else { //任务执行失败需要通知调用者,让调用者自己决策是否需要重新调度还是放弃,这样worker和主线程实现了解偶 log.Println("error set inventory") err = errors.New("inventory err") goto Error } } else { log.Printf("error DLock error :%d\n", inventory) err = errors.New("inventory err") goto Error } lock.Unlock() response.Id = request.Id return nil Error: lock.Unlock() return err }