2
0
mirror of https://github.com/hibiken/asynq.git synced 2024-11-10 03:21:55 +08:00
golang基于redis的异步队列
Go to file
dependabot[bot] e6f74c1c2b
Bump golang.org/x/text from 0.3.7 to 0.3.8 in /tools (#619)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.3.7 to 0.3.8.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.3.7...v0.3.8)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-24 15:12:45 +08:00
.github Update FUNDING.yml 2023-07-07 20:54:12 -07:00
docs/assets Update readme 2022-06-03 04:14:45 -07:00
internal feat (add): panic error handling (#491) 2023-07-20 21:33:39 +08:00
tools Bump golang.org/x/text from 0.3.7 to 0.3.8 in /tools (#619) 2023-07-24 15:12:45 +08:00
x Bump github.com/prometheus/client_golang from 1.11.0 to 1.11.1 in /x (#615) 2023-07-24 11:43:45 +08:00
.gitignore Allow configuration of DelayedTaskCheckInterval 2022-01-03 14:44:00 -08:00
aggregator_test.go Define GroupAggregator interface 2022-04-11 16:55:43 -07:00
aggregator.go Always enqueue the aggregated task in the same queue 2022-04-11 16:55:43 -07:00
asynq_test.go Update redis/go-redis to v9 2023-04-17 22:30:33 -07:00
asynq.go Update redis/go-redis to v9 2023-04-17 22:30:33 -07:00
benchmark_test.go Rename asynqtest package to testutil 2022-04-11 16:55:43 -07:00
CHANGELOG.md v0.24.1 2023-05-01 06:48:07 -07:00
client_test.go Rename asynqtest package to testutil 2022-04-11 16:55:43 -07:00
client.go Update redis/go-redis to v9 2023-04-17 22:30:33 -07:00
CODE_OF_CONDUCT.md Add CODE_OF_CONDUCT.md 2022-01-04 06:17:48 -08:00
context.go Fix comment typos 2022-05-16 21:14:15 -07:00
CONTRIBUTING.md Update CONTRIBUTING.md to use git ssh 2022-05-08 09:21:33 -07:00
doc.go Update Client.Enqueue to return TaskInfo 2021-06-29 16:34:21 -07:00
example_test.go Add example for ResultWriter 2022-01-31 09:08:41 -08:00
forwarder_test.go Rename asynqtest package to testutil 2022-04-11 16:55:43 -07:00
forwarder.go Update forwarder to use time.Timer 2022-04-11 16:55:43 -07:00
go.mod fix: breaking build below go < 1.18 2023-04-17 22:30:33 -07:00
go.sum fix: breaking build below go < 1.18 2023-04-17 22:30:33 -07:00
healthcheck_test.go Change Server API 2021-06-29 16:34:21 -07:00
healthcheck.go Change Server API 2021-06-29 16:34:21 -07:00
heartbeat_test.go fix some typos 2023-01-05 20:03:02 -08:00
heartbeat.go Update log messages 2022-02-19 06:15:44 -08:00
inspector_test.go fix unit test 2023-04-17 22:30:33 -07:00
inspector.go Update redis/go-redis to v9 2023-04-17 22:30:33 -07:00
janitor_test.go Rename asynqtest package to testutil 2022-04-11 16:55:43 -07:00
janitor.go Update log messages 2022-02-19 06:15:44 -08:00
LICENSE Add MIT License 2019-11-30 10:21:25 -08:00
Makefile Refactor redis keys and store messages in protobuf 2021-06-29 16:34:21 -07:00
periodic_task_manager_test.go Create PeriodicTaskManager 2022-01-22 05:59:33 -08:00
periodic_task_manager.go Create PeriodicTaskManager 2022-01-22 05:59:33 -08:00
processor_test.go feat (add): panic error handling (#491) 2023-07-20 21:33:39 +08:00
processor.go feat (add): panic error handling (#491) 2023-07-20 21:33:39 +08:00
README.md Update README.md to include sponsoring section 2023-07-07 21:00:05 -07:00
recoverer_test.go Rename asynqtest package to testutil 2022-04-11 16:55:43 -07:00
recoverer.go fix some typos 2023-01-05 20:03:02 -08:00
scheduler_test.go Add pre and post enqueue callback options for Scheduler 2022-05-27 10:50:02 -07:00
scheduler.go Update redis/go-redis to v9 2023-04-17 22:30:33 -07:00
servemux_test.go Change payload to byte slice 2021-06-29 16:34:21 -07:00
servemux.go chore: replace loop with mux.mws = append(mux.mws, mws...) 2023-07-07 21:01:54 -07:00
server_test.go completely update Redis package 2023-04-17 22:30:33 -07:00
server.go feat (add): panic error handling (#491) 2023-07-20 21:33:39 +08:00
signals_unix.go Change Server API 2021-06-29 16:34:21 -07:00
signals_windows.go Add Scheduler 2020-10-13 06:31:47 -07:00
subscriber_test.go Change Server API 2021-06-29 16:34:21 -07:00
subscriber.go Update redis/go-redis to v9 2023-04-17 22:30:33 -07:00
syncer_test.go Rename asynqtest package to testutil 2022-04-11 16:55:43 -07:00
syncer.go Change Server API 2021-06-29 16:34:21 -07:00

Asynq logo

Simple, reliable & efficient distributed task queue in Go

GoDoc Go Report Card Build Status License: MIT Gitter chat

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 tasks on a queue
  • Server pulls tasks 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.

Example use case

Task Queue Diagram

Features

Stability and Compatibility

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

☝️ Important Note: Current major version is zero (v0.x.x) to accommodate 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.

Sponsoring

If you are using this package in production, please consider sponsoring the project to show your support!

Quickstart

Make sure you have Go installed (download). Version 1.14 or higher is required.

Initialize your project by creating a folder and then running go mod init github.com/your/repo (learn more) inside the folder. Then install Asynq library with the go get command:

go get -u github.com/hibiken/asynq

Make sure you're running a Redis server locally or from a Docker container. Version 4.0 or higher is required.

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

package tasks

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "time"
    "github.com/hibiken/asynq"
)

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

type EmailDeliveryPayload struct {
    UserID     int
    TemplateID string
}

type ImageResizePayload struct {
    SourceURL string
}

//----------------------------------------------
// 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, error) {
    payload, err := json.Marshal(EmailDeliveryPayload{UserID: userID, TemplateID: tmplID})
    if err != nil {
        return nil, err
    }
    return asynq.NewTask(TypeEmailDelivery, payload), nil
}

func NewImageResizeTask(src string) (*asynq.Task, error) {
    payload, err := json.Marshal(ImageResizePayload{SourceURL: src})
    if err != nil {
        return nil, err
    }
    // task options can be passed to NewTask, which can be overridden at enqueue time.
    return asynq.NewTask(TypeImageResize, payload, asynq.MaxRetry(5), asynq.Timeout(20 * time.Minute)), nil
}

//---------------------------------------------------------------
// 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 {
    var p EmailDeliveryPayload
    if err := json.Unmarshal(t.Payload(), &p); err != nil {
        return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
    }
    log.Printf("Sending Email to User: user_id=%d, template_id=%s", p.UserID, p.TemplateID)
    // Email delivery code ...
    return nil
}

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

func (processor *ImageProcessor) ProcessTask(ctx context.Context, t *asynq.Task) error {
    var p ImageResizePayload
    if err := json.Unmarshal(t.Payload(), &p); err != nil {
        return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
    }
    log.Printf("Resizing image: src=%s", p.SourceURL)
    // Image resizing code ...
    return nil
}

func NewImageProcessor() *ImageProcessor {
	return &ImageProcessor{}
}

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

package main

import (
    "log"
    "time"

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

const redisAddr = "127.0.0.1:6379"

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

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

    task, err := tasks.NewEmailDeliveryTask(42, "some:template:id")
    if err != nil {
        log.Fatalf("could not create task: %v", err)
    }
    info, err := client.Enqueue(task)
    if err != nil {
        log.Fatalf("could not enqueue task: %v", err)
    }
    log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)


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

    info, err = client.Enqueue(task, asynq.ProcessIn(24*time.Hour))
    if err != nil {
        log.Fatalf("could not schedule task: %v", err)
    }
    log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)


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

    task, err = tasks.NewImageResizeTask("https://example.com/myassets/image.jpg")
    if err != nil {
        log.Fatalf("could not create task: %v", err)
    }
    info, err = client.Enqueue(task, asynq.MaxRetry(10), asynq.Timeout(3 * time.Minute))
    if err != nil {
        log.Fatalf("could not enqueue task: %v", err)
    }
    log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)
}

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() {
    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)
    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 the package godoc.

Web UI

Asynqmon is a web based tool for monitoring and administrating Asynq queues and tasks.

Here's a few screenshots of the Web UI:

Queues view

Web UI Queues View

Tasks view

Web UI TasksView

Metrics view Screen Shot 2021-12-19 at 4 37 19 PM

Settings and adaptive dark mode

Web UI Settings and adaptive dark mode

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

Command Line Tool

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

To install the CLI tool, run the following command:

go install github.com/hibiken/asynq/tools/asynq

Here's an example of running the asynq dash command:

Gif

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

Contributing

We are open to, and grateful for, any contributions (GitHub issues/PRs, feedback on Gitter channel, etc) made by the community.

Please see the Contribution Guide before contributing.

License

Copyright (c) 2019-present Ken Hibino and Contributors. Asynq is free and open-source software licensed under the MIT License. Official logo was created by Vic Shóstak and distributed under Creative Commons license (CC0 1.0 Universal).