244 lines
5.4 KiB
Go
244 lines
5.4 KiB
Go
package redis
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"time"
|
||
|
||
modtask "go-common/app/admin/main/aegis/model/task"
|
||
"go-common/library/cache/redis"
|
||
"go-common/library/ecode"
|
||
"go-common/library/log"
|
||
|
||
"github.com/pkg/errors"
|
||
)
|
||
|
||
func lockKey(businessID, flowID int64) string {
|
||
return fmt.Sprintf("aegis_lock_%d_%d", businessID, flowID)
|
||
}
|
||
|
||
func (d *Dao) getlock(c context.Context, bizid, flowid int64) (ok bool) {
|
||
var (
|
||
conn = d.cluster.Get(c)
|
||
key = lockKey(bizid, flowid)
|
||
err error
|
||
)
|
||
defer conn.Close()
|
||
|
||
if ok, err = redis.Bool(conn.Do("SETNX", key, "1")); err != nil {
|
||
if err == redis.ErrNil {
|
||
err = nil
|
||
} else {
|
||
log.Error("conn.Do(SETNX(%s)) error(%v)", key, err)
|
||
return
|
||
}
|
||
}
|
||
|
||
if ok {
|
||
conn.Do("EXPIRE", key, 3)
|
||
}
|
||
return
|
||
}
|
||
|
||
//SeizeTask .
|
||
func (d *Dao) SeizeTask(c context.Context, businessID, flowID, uid, count int64) (hitids []int64, missids []int64, others map[int64]int64, err error) {
|
||
var (
|
||
lock bool
|
||
pubkey = publicKey(businessID, flowID)
|
||
ids []int64
|
||
)
|
||
|
||
// 1. 抢占分布式锁
|
||
for lc := 0; lc < 3; lc++ {
|
||
if lock = d.getlock(c, businessID, flowID); lock {
|
||
break
|
||
}
|
||
time.Sleep(10 * time.Millisecond)
|
||
}
|
||
|
||
if !lock {
|
||
log.Error("getlock getlock fail(%d,%d,%d)", businessID, flowID, uid)
|
||
err = ecode.AegisTaskBusy
|
||
return
|
||
}
|
||
|
||
conn := d.cluster.Get(c)
|
||
defer conn.Close()
|
||
defer conn.Do("DEL", lockKey(businessID, flowID))
|
||
|
||
var (
|
||
head, tail = int64(0), int64(count)
|
||
)
|
||
// 2. 从 public 按权重从高到低取出一批来
|
||
for {
|
||
if ids, err = redis.Int64s(conn.Do("ZRANGE", pubkey, head, tail)); err != nil {
|
||
log.Error("redis (ZRANGE,%s,%d,%d) error(%v)", pubkey, 0, count, err)
|
||
return
|
||
}
|
||
head += count
|
||
tail += count
|
||
if len(ids) == 0 {
|
||
break
|
||
}
|
||
|
||
for _, id := range ids {
|
||
if err = conn.Send("GET", haskKey(id)); err != nil {
|
||
log.Error("redis (GET,%s) error(%v)", haskKey(id), err)
|
||
return
|
||
}
|
||
}
|
||
conn.Flush()
|
||
|
||
var enough bool
|
||
|
||
for _, id := range ids {
|
||
var (
|
||
bs []byte
|
||
e error
|
||
)
|
||
bs, e = redis.Bytes(conn.Receive())
|
||
|
||
if e != nil {
|
||
log.Error("Receive Weight(%d) error(%v)", id, errors.WithStack(e))
|
||
missids = append(missids, id)
|
||
continue
|
||
}
|
||
|
||
task := &modtask.Task{}
|
||
if e = json.Unmarshal(bs, task); err != nil {
|
||
log.Error("json.Unmarshal error(%v)", errors.WithStack(e))
|
||
missids = append(missids, id)
|
||
continue
|
||
}
|
||
if task.ID != id {
|
||
log.Error("id(%d-%d)不匹配", task.ID, id)
|
||
missids = append(missids, id)
|
||
continue
|
||
}
|
||
|
||
if task.UID != 0 && task.UID != uid {
|
||
log.Info("id(%d) 任务已经指派给(%d)", task.ID, task.UID)
|
||
missids = append(missids, id)
|
||
continue
|
||
}
|
||
|
||
hitids = append(hitids, id)
|
||
|
||
if len(hitids) >= int(count) {
|
||
enough = true
|
||
break
|
||
}
|
||
}
|
||
if enough {
|
||
break
|
||
}
|
||
}
|
||
|
||
personKey := personalKey(businessID, flowID, uid)
|
||
for _, id := range hitids {
|
||
conn.Send("ZREM", pubkey, formatID(id))
|
||
conn.Send("LREM", personKey, 0, id)
|
||
conn.Send("RPUSH", personKey, id)
|
||
}
|
||
conn.Flush()
|
||
for i := 0; i < len(hitids)*3; i++ {
|
||
conn.Receive()
|
||
}
|
||
|
||
log.Info("rangefunc count(%d) hitids(%v) missids(%v)", count, hitids, missids)
|
||
return
|
||
}
|
||
|
||
/*
|
||
遍历personal,delay,public。
|
||
在缓存中进行状态校验,public还要补充缓存权重
|
||
*/
|
||
func (d *Dao) rangefuncCluster(c context.Context, listtype string, opt *modtask.ListOptions) (tasks map[int64]*modtask.Task, count int64, hitids, missids []int64, err error) {
|
||
var (
|
||
key string
|
||
LENCMD, RANGECMD = "LLEN", "LRANGE"
|
||
ids []int64
|
||
)
|
||
|
||
conn := d.cluster.Get(c)
|
||
defer conn.Close()
|
||
|
||
switch listtype {
|
||
case "public":
|
||
LENCMD, RANGECMD = "ZCARD", "ZRANGE"
|
||
key = publicKey(opt.BusinessID, opt.FlowID)
|
||
case "personal":
|
||
key = personalKey(opt.BusinessID, opt.FlowID, opt.UID)
|
||
case "delay":
|
||
key = delayKey(opt.BusinessID, opt.FlowID, opt.UID)
|
||
}
|
||
|
||
// 1. 长度
|
||
if count, err = redis.Int64(conn.Do(LENCMD, key)); err != nil {
|
||
log.Error("redis (%s,%s) error(%v)", LENCMD, key, err)
|
||
return
|
||
}
|
||
if count == 0 {
|
||
return
|
||
}
|
||
|
||
if ids, err = redis.Int64s(conn.Do(RANGECMD, key, (opt.Pn-1)*opt.Ps, opt.Pn*opt.Ps-1)); err != nil {
|
||
log.Error("redis (%s,%s,%d,%d) error(%v)", LENCMD, key, (opt.Pn-1)*opt.Ps, opt.Pn*opt.Ps, err)
|
||
return
|
||
}
|
||
|
||
for _, id := range ids {
|
||
if err = conn.Send("GET", haskKey(id)); err != nil {
|
||
log.Error("redis (GET,%s) error(%v)", haskKey(id), err)
|
||
return
|
||
}
|
||
if listtype == "public" {
|
||
if err = conn.Send("ZSCORE", key, formatID(id)); err != nil {
|
||
log.Error("redis (ZSCORE,%s,%s) error(%v)", key, formatID(id), err)
|
||
return
|
||
}
|
||
}
|
||
}
|
||
conn.Flush()
|
||
|
||
tasks = make(map[int64]*modtask.Task)
|
||
for _, id := range ids {
|
||
var (
|
||
bs []byte
|
||
e error
|
||
wt int64
|
||
)
|
||
bs, e = redis.Bytes(conn.Receive())
|
||
if listtype == "public" {
|
||
wt, _ = redis.Int64(conn.Receive())
|
||
wt = -wt
|
||
}
|
||
|
||
if e != nil {
|
||
log.Error("Receive Weight(%d) error(%v)", id, errors.WithStack(e))
|
||
missids = append(missids, id)
|
||
continue
|
||
}
|
||
|
||
task := &modtask.Task{}
|
||
if e = json.Unmarshal(bs, task); err != nil {
|
||
log.Error("json.Unmarshal error(%v)", errors.WithStack(e))
|
||
missids = append(missids, id)
|
||
continue
|
||
}
|
||
if task.ID != id {
|
||
log.Error("id(%d-%d)不匹配", task.ID, id)
|
||
missids = append(missids, id)
|
||
continue
|
||
}
|
||
// 缓存里状态同步不实时,不能用作校验
|
||
|
||
tasks[task.ID] = task
|
||
hitids = append(hitids, id)
|
||
}
|
||
|
||
log.Info("rangefunc count(%d) hitids(%v) missids(%v)", count, hitids, missids)
|
||
return
|
||
}
|