2
0
mirror of https://github.com/hibiken/asynq.git synced 2024-11-15 11:58:47 +08:00
golang基于redis的异步队列
Go to file
2021-04-04 12:51:00 -07:00
.github Run CI build with go1.16 2021-02-25 09:31:17 -08:00
docs/assets Add Web UI section in README 2021-02-01 17:01:04 -08:00
inspeq Use db13 for inspeq package testing 2021-01-31 06:09:40 -08:00
internal v0.17.1 2021-04-04 12:51:00 -07:00
tools Fix CLI build 2021-01-31 06:09:40 -08:00
.gitignore Update all reference to asynqmon to Asynq CLI 2020-04-19 08:51:17 -07:00
asynq_test.go Add test flags to run tests using redis cluster 2020-09-12 12:59:03 -07:00
asynq.go Add dial, read, write timeout options to RedisConnOpt 2021-03-24 16:49:04 -07:00
benchmark_test.go Close redis client after each test run 2020-09-12 12:59:03 -07:00
CHANGELOG.md v0.17.1 2021-04-04 12:51:00 -07:00
client_test.go Move all inspector related code to subpackage inspeq 2021-01-31 06:09:40 -08:00
client.go Update RedisConnOpt interface 2021-01-31 06:09:40 -08:00
context_test.go Add GetQueueName helper to extract queue name from context 2020-09-12 12:59:03 -07:00
context.go Add GetQueueName helper to extract queue name from context 2020-09-12 12:59:03 -07:00
CONTRIBUTING.md Mention about testing using redis cluster in CONTRIBUTING.md 2020-09-12 12:59:03 -07:00
doc.go Update package documentation 2020-09-12 12:59:03 -07:00
example_test.go Update docs for periodic tasks feature 2020-10-13 06:31:47 -07:00
forwarder_test.go Add Scheduler 2020-10-13 06:31:47 -07:00
forwarder.go Add Scheduler 2020-10-13 06:31:47 -07:00
go.mod Add Scheduler 2020-10-13 06:31:47 -07:00
go.sum Add Scheduler 2020-10-13 06:31:47 -07:00
healthcheck_test.go Close redis client after each test run 2020-09-12 12:59:03 -07:00
healthcheck.go Add healthchecker to check broker connection 2020-07-28 22:45:57 -07:00
heartbeat_test.go Record deadline within WorkerInfo 2021-01-31 06:09:40 -08:00
heartbeat.go Record deadline within WorkerInfo 2021-01-31 06:09:40 -08:00
LICENSE Add MIT License 2019-11-30 10:21:25 -08:00
payload_test.go Add String and MarshalJSON methods to Payload type 2020-09-20 07:33:23 -07:00
payload.go Add String and MarshalJSON methods to Payload type 2020-09-20 07:33:23 -07:00
processor_test.go Record deadline within WorkerInfo 2021-01-31 06:09:40 -08:00
processor.go Record deadline within WorkerInfo 2021-01-31 06:09:40 -08:00
README.md Add Web UI section in README 2021-02-01 17:01:04 -08:00
recoverer_test.go Rename DeadTask to ArchivedTask and action "kill" to "archive" 2021-01-14 06:43:44 -08:00
recoverer.go Export DefaultRetryDelayFunc 2021-01-14 06:43:44 -08:00
scheduler_test.go Add Unregister method to Scheduler 2021-03-10 20:38:44 -08:00
scheduler.go Add Unregister method to Scheduler 2021-03-10 20:38:44 -08:00
servemux_test.go Add Use method to better support middlewares with ServeMux 2020-03-13 14:13:17 -07:00
servemux.go remove typo and redundant code 2020-05-22 05:11:54 -07:00
server_test.go Minor fix 2021-03-07 16:27:14 -08:00
server.go Update RedisConnOpt interface 2021-01-31 06:09:40 -08:00
signals_unix.go Add Scheduler 2020-10-13 06:31:47 -07:00
signals_windows.go Add Scheduler 2020-10-13 06:31:47 -07:00
subscriber_test.go Close redis client after each test run 2020-09-12 12:59:03 -07:00
subscriber.go Rename InProgress to Active 2020-09-12 12:59:03 -07:00
syncer_test.go Close redis client after each test run 2020-09-12 12:59:03 -07:00
syncer.go Add deadline to syncRequest 2020-07-06 05:48:31 -07:00

Asynq

Build Status GoDoc Go Report Card License: MIT Gitter chat

Overview

Asynq is a Go library for queueing tasks and processing them asynchronously with workers. It's backed by Redis and is designed to be scalable yet easy to get started.

Highlevel overview of how Asynq works:

  • Client puts task on a queue
  • Server pulls task off queues and starts a worker goroutine for each task
  • Tasks are processed concurrently by multiple workers

Task queues are used as a mechanism to distribute work across multiple machines.
A system can consist of multiple worker servers and brokers, giving way to high availability and horizontal scaling.

Task Queue Diagram

Stability and Compatibility

Important Note: Current major version is zero (v0.x.x) to accomodate rapid development and fast iteration while getting early feedback from users (Feedback on APIs are appreciated!). The public API could change without a major version update before v1.0.0 release.

Status: The library is currently undergoing heavy development with frequent, breaking API changes.

Features

Quickstart

First, make sure you are running a Redis server locally.

$ redis-server

Next, write a package that encapsulates task creation and task handling.

package tasks

import (
    "fmt"

    "github.com/hibiken/asynq"
)

// A list of task types.
const (
    TypeEmailDelivery   = "email:deliver"
    TypeImageResize     = "image:resize"
)

//----------------------------------------------
// Write a function NewXXXTask to create a task.
// A task consists of a type and a payload.
//----------------------------------------------

func NewEmailDeliveryTask(userID int, tmplID string) *asynq.Task {
    payload := map[string]interface{}{"user_id": userID, "template_id": tmplID}
    return asynq.NewTask(TypeEmailDelivery, payload)
}

func NewImageResizeTask(src string) *asynq.Task {
    payload := map[string]interface{}{"src": src}
    return asynq.NewTask(TypeImageResize, payload)
}

//---------------------------------------------------------------
// Write a function HandleXXXTask to handle the input task.
// Note that it satisfies the asynq.HandlerFunc interface.
//
// Handler doesn't need to be a function. You can define a type
// that satisfies asynq.Handler interface. See examples below.
//---------------------------------------------------------------

func HandleEmailDeliveryTask(ctx context.Context, t *asynq.Task) error {
    userID, err := t.Payload.GetInt("user_id")
    if err != nil {
        return err
    }
    tmplID, err := t.Payload.GetString("template_id")
    if err != nil {
        return err
    }
    fmt.Printf("Send Email to User: user_id = %d, template_id = %s\n", userID, tmplID)
    // Email delivery code ...
    return nil
}

// ImageProcessor implements asynq.Handler interface.
type ImageProcessor struct {
    // ... fields for struct
}

func (p *ImageProcessor) ProcessTask(ctx context.Context, t *asynq.Task) error {
    src, err := t.Payload.GetString("src")
    if err != nil {
        return err
    }
    fmt.Printf("Resize image: src = %s\n", src)
    // Image resizing code ...
    return nil
}

func NewImageProcessor() *ImageProcessor {
    // ... return an instance
}

In your application code, import the above package and use Client to put tasks on the queue.

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/hibiken/asynq"
    "your/app/package/tasks"
)

const redisAddr = "127.0.0.1:6379"

func main() {
    r := asynq.RedisClientOpt{Addr: redisAddr}
    c := asynq.NewClient(r)
    defer c.Close()

    // ------------------------------------------------------
    // Example 1: Enqueue task to be processed immediately.
    //            Use (*Client).Enqueue method.
    // ------------------------------------------------------

    t := tasks.NewEmailDeliveryTask(42, "some:template:id")
    res, err := c.Enqueue(t)
    if err != nil {
        log.Fatal("could not enqueue task: %v", err)
    }
    fmt.Printf("Enqueued Result: %+v\n", res)


    // ------------------------------------------------------------
    // Example 2: Schedule task to be processed in the future.
    //            Use ProcessIn or ProcessAt option.
    // ------------------------------------------------------------

    t = tasks.NewEmailDeliveryTask(42, "other:template:id")
    res, err = c.Enqueue(t, asynq.ProcessIn(24*time.Hour))
    if err != nil {
        log.Fatal("could not schedule task: %v", err)
    }
    fmt.Printf("Enqueued Result: %+v\n", res)


    // ----------------------------------------------------------------------------
    // Example 3: Set other options to tune task processing behavior.
    //            Options include MaxRetry, Queue, Timeout, Deadline, Unique etc.
    // ----------------------------------------------------------------------------

    c.SetDefaultOptions(tasks.TypeImageResize, asynq.MaxRetry(10), asynq.Timeout(3*time.Minute))

    t = tasks.NewImageResizeTask("some/blobstore/path")
    res, err = c.Enqueue(t)
    if err != nil {
        log.Fatal("could not enqueue task: %v", err)
    }
    fmt.Printf("Enqueued Result: %+v\n", res)

    // ---------------------------------------------------------------------------
    // Example 4: Pass options to tune task processing behavior at enqueue time.
    //            Options passed at enqueue time override default ones, if any.
    // ---------------------------------------------------------------------------

    t = tasks.NewImageResizeTask("some/blobstore/path")
    res, err = c.Enqueue(t, asynq.Queue("critical"), asynq.Timeout(30*time.Second))
    if err != nil {
        log.Fatal("could not enqueue task: %v", err)
    }
    fmt.Printf("Enqueued Result: %+v\n", res)
}

Next, start a worker server to process these tasks in the background.
To start the background workers, use Server and provide your Handler to process the tasks.

You can optionally use ServeMux to create a handler, just as you would with "net/http" Handler.

package main

import (
    "log"

    "github.com/hibiken/asynq"
    "your/app/package/tasks"
)

const redisAddr = "127.0.0.1:6379"

func main() {
    r := asynq.RedisClientOpt{Addr: redisAddr}

    srv := asynq.NewServer(r, 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)
    mux.Handle(tasks.TypeImageResize, tasks.NewImageProcessor())
    // ...register other handlers...

    if err := srv.Run(mux); err != nil {
        log.Fatalf("could not run server: %v", err)
    }
}

For a more detailed walk-through of the library, see our Getting Started Guide.

To Learn more about asynq features and APIs, see our Wiki and godoc.

Web UI

Asynqmon is a web based tool for monitoring and administrating Asynq queues and tasks. Please see the tool's README for details.

Here's a few screenshots of the web UI.

Queues view
Web UI QueuesView

Tasks view
Web UI TasksView

Command Line Tool

Asynq ships with a command line tool to inspect the state of queues and tasks.

Here's an example of running the stats command.

Gif

For details on how to use the tool, refer to the tool's README.

Installation

To install asynq library, run the following command:

go get -u github.com/hibiken/asynq

To install the CLI tool, run the following command:

go get -u github.com/hibiken/asynq/tools/asynq

Requirements

Dependency Version
Redis v3.0+
Go v1.13+

Contributing

We are open to, and grateful for, any contributions (Github issues/pull-requests, feedback on Gitter channel, etc) made by the community. Please see the Contribution Guide before contributing.

Acknowledgements

  • Sidekiq : Many of the design ideas are taken from sidekiq and its Web UI
  • RQ : Client APIs are inspired by rq library.
  • Cobra : Asynq CLI is built with cobra

License

Asynq is released under the MIT license. See LICENSE.