73 lines
1.8 KiB
Go
73 lines
1.8 KiB
Go
package worker
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
"goRedisDLM/engine"
|
||
"goRedisDLM/redisLock"
|
||
"log"
|
||
"math/rand"
|
||
"strconv"
|
||
"time"
|
||
|
||
"github.com/go-redis/redis/v8"
|
||
)
|
||
|
||
type Inventory struct {
|
||
Client *redis.Client
|
||
}
|
||
|
||
func (i *Inventory) Work(request engine.Request, response *engine.Response) error {
|
||
ctx := context.Background()
|
||
|
||
lock := redisLock.CreateRedisLock(i.Client, ctx, request.Dlock, 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 lock.IsHeld() {
|
||
setString := fmt.Sprintf("%d", inventory)
|
||
if i.Client.Set(ctx, "inventory", setString, redis.KeepTTL).Val() != "OK" {
|
||
log.Println("error set inventory")
|
||
err = errors.New("inventory err")
|
||
goto Error
|
||
}
|
||
} else {
|
||
//任务执行失败需要通知调用者,让调用者自己决策是否需要重新调度还是放弃,这样worker和主线程实现了解偶
|
||
log.Println("error lock is not held")
|
||
err = errors.New("error lock is not held")
|
||
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
|
||
}
|