golang的包asynq定时任务

511 阅读2分钟

问题:

  1. 是否支持失败重试?
  2. 是否支持图形化ui?
  3. 是否有监控体系?

优秀版:asynq包

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

  • 答案1
  • 答案2
  • 答案3

定时任务:

client代码

package main

import (
   "asynq_test/tasks"
   "log"

   "github.com/hibiken/asynq"
)

const redisAddr = "127.0.0.1:6379"

func main() {
   // 周期性任务
   scheduler := asynq.NewScheduler(
      asynq.RedisClientOpt{
         Addr: redisAddr,
      }, nil)

   task, err := tasks.NewEmailDeliveryTask(42, "some:template:id")
   if err != nil {
      log.Fatalf("could not create task: %v", err)
   }
   // 每隔1分钟同步一次
   entryID, err := scheduler.Register("@every 1s", task)

   if err != nil {
      log.Fatal(err)
   }
   log.Printf("registered an entry: %q\n", entryID)

   if err := scheduler.Run(); err != nil {
      log.Fatal(err)
   }
}

server代码:

package main

import (
   "asynq_test/tasks"
   "log"

   "github.com/hibiken/asynq"
)

const redisAddr = "127.0.0.1:6379"

func main() {
   srv := asynq.NewServer(
      asynq.RedisClientOpt{Addr: redisAddr},
      asynq.Config{
         // Specify how many concurrent workers to use
         Concurrency: 10,
         // Optionally specify multiple queues with different priority.
         Queues: map[string]int{
            "critical": 6,
            "default":  3,
            "low":      1,
         },
         // See the godoc for other configuration options
      },
   )

   // mux maps a type to a handler
   mux := asynq.NewServeMux()
   mux.HandleFunc(tasks.TypeEmailDelivery, tasks.HandleEmailDeliveryTask)
   if err := srv.Run(mux); err != nil {
      log.Fatalf("could not run server: %v", err)
   }
}
  • client端函数
    • func NewScheduler(r RedisConnOpt, opts *SchedulerOpts) *Scheduler
    • func NewEmailDeliveryTask(userID int, tmplID string) (*asynq.Task, error)
    • func (s *Scheduler) Register(cronspec string, task *Task, opts ...Option)
    • func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error)
    • func (s *Scheduler) Run() error
    • func (s *Scheduler) Start() error
    • func (c *Cron) Start()
    • func (j *enqueueJob) Run()
    • 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) enqueue(ctx context.Context, msg *base.TaskMessage, uniqueTTL time.Duration) error
    • func (r *RDB) Enqueue(ctx context.Context, msg *base.TaskMessage) error
    • SAdd(ctx context.Context, key string, members ...interface{}) *IntCmd
    • 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) Run(handler Handler) error
    • func (srv *Server) Start(handler Handler) error
    • func (p *processor) start(wg *sync.WaitGroup)
    • func (p *processor) exec()
    • Dequeue(qnames ...string) (*TaskMessage, time.Time, error)
    • func newTask(typename string, payload []byte, w *ResultWriter) *Task
    • func (p *processor) perform(ctx context.Context, task *Task) (err error)
    • func (fn HandlerFunc) ProcessTask(ctx context.Context, task *Task) error

此定时任务底层利用了cron包提供的能力

client相关的lua脚本

var enqueueCmd = redis.NewScript(`
if redis.call("EXISTS", KEYS[1]) == 1 then
	return 0
end
redis.call("HSET", KEYS[1],
           "msg", ARGV[1],
           "state", "pending",
           "pending_since", ARGV[3])
redis.call("LPUSH", KEYS[2], ARGV[2])
return 1
`)

server相关的lua脚本

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`)