golang延迟队列

80 阅读1分钟

简单版:

func (dao *defaultRedisDao) ZAdd(ctx context.Context, key, member string, delayTime float64) error {

   z := &redis.Z{
      Member: member,
      Score:  delayTime + float64(time.Now().Unix()),
   }
   err := dao.cache.ZAdd(ctx, key, z).Err()
   if err != nil {
      return errors.Wrapf(err, "ZAdd failed key:%s, z:%+v", key, *z)
   }
   return nil
}
opt := &redis.ZRangeBy{
   Min:    "0",
   Max:    cast.ToString(time.Now().Unix()),
   Offset: 0,
   Count:  1000,
}
batch, err := l.redisDao.ZRangeByScoreWithScores(ctx, robotdao.ZAddKey, opt)

升级版:

git地址:github.com/hibiken/asy…

延时队列

  • client端函数
    • func NewClient(r RedisConnOpt) *Client
    • func NewEmailDeliveryTask(userID int, tmplID string) (*asynq.Task, error)
    • func (c *Client) Enqueue(task *Task, opts ...Option) (*TaskInfo, error)
    • func (c *Client) EnqueueContext(ctx context.Context, task *Task, opts ...Option) (*TaskInfo, error)
    • func (c *Client) schedule(ctx context.Context, msg *base.TaskMessage, t time.Time, uniqueTTL time.Duration) error
    • func (r *RDB) Schedule(ctx context.Context, msg *base.TaskMessage, processAt time.Time) error
    • func (r *RDB) runScriptWithErrorCode(ctx context.Context, op errors.Op, script *redis.Script, keys []string, args ...interface{}) (int64, error)
    • func (s *Scheduler) waitForSignals()
    • func (s *Scheduler) Shutdown()
    • func (c *Cron) Stop() context.Context
  • server端函数
    • func NewServer(r RedisConnOpt, cfg Config) *Server
    • func NewServeMux() *ServeMux
    • func (mux *ServeMux) HandleFunc(pattern string, handler func(context.Context, *Task) error)
    • func (mux *ServeMux) Handle(pattern string, handler Handler)
    • func (srv *Server) Start(handler Handler) error
    • func (f *forwarder) start(wg *sync.WaitGroup)
    • func (r *RDB) ForwardIfReady(qnames ...string) error
    • func (r *RDB) forwardAll(qname string) (err error)
    • func (r *RDB) forward(delayedKey, pendingKey, taskKeyPrefix, groupKeyPrefix string) (int, error)

client相关的lua脚本

keys := []string{
        base.TaskKey(msg.Queue, msg.ID),
        base.ScheduledKey(msg.Queue),
}
argv := []interface{}{
        encoded,
        processAt.Unix(),
        msg.ID,
}
var scheduleCmd = redis.NewScript(`
if redis.call("EXISTS", KEYS[1]) == 1 then
   return 0
end
redis.call("HSET", KEYS[1],
           "msg", ARGV[1],
           "state", "scheduled")
redis.call("ZADD", KEYS[2], ARGV[2], ARGV[3])
return 1
`)

server相关的lua脚本

local ids = redis.call("ZRANGEBYSCORE", KEYS[1], "-inf", ARGV[1], "LIMIT", 0, 100)
for _, id in ipairs(ids) do
   local taskKey = ARGV[2] .. id
   local group = redis.call("HGET", taskKey, "group")
   if group and group ~= '' then
       redis.call("ZADD", ARGV[4] .. group, ARGV[1], id)
      redis.call("ZREM", KEYS[1], id)
      redis.call("HSET", taskKey,
               "state", "aggregating")
   else
      redis.call("LPUSH", KEYS[2], id)
      redis.call("ZREM", KEYS[1], id)
      redis.call("HSET", taskKey,
               "state", "pending",
               "pending_since", ARGV[3])
   end
end
return table.getn(ids)`)
keys := []string{
        base.PendingKey(qname),
        base.PausedKey(qname),
        base.ActiveKey(qname),
        base.LeaseKey(qname),
}
var dequeueCmd = redis.NewScript(`
if redis.call("EXISTS", KEYS[2]) == 0 then
   local id = redis.call("RPOPLPUSH", KEYS[1], KEYS[3])
   if id then
      local key = ARGV[2] .. id
      redis.call("HSET", key, "state", "active")
      redis.call("HDEL", key, "pending_since")
      redis.call("ZADD", KEYS[4], ARGV[1], id)
      return redis.call("HGET", key, "msg")
   end
end
return nil`)