Asynq Distributed Task Queue for Go

repository·master·Indexed 12 days ago

https://github.com/hibiken/asynq

A high-performance distributed task queue for Go backed by Redis. It features a producer-consumer model with a Client for enqueuing tasks and a Server for processing them via worker goroutines. Supports retries, scheduling, priority queues, and a CLI tool for monitoring queue states and task statistics.

Tokens
20K
Snippets
81
Records
100
Agent score
95%

What's inside Asynq

  1. How Asynq works

    master

    Asynq is a distributed task queue for Go that uses Redis as a backend. The workflow follows a producer-consumer model:

    1. Client: A producer puts tasks onto a queue.
    2. Server: A worker server pulls tasks off the queues.
    3. Workers: The server starts a worker goroutine for each task, allowing for concurrent processing.

    This architecture allows for horizontal scaling by deploying multiple worker servers and brokers across different machines.

  2. Use the Asynq CLI commands

    master

    The Asynq CLI is used to monitor queues and tasks managed by the asynq package. You can view details for any specific command by running asynq help <command> <subcommand>.

    Available Commands

    • asynq dash: Opens a dashboard interface.
    • asynq stats: Displays statistics.
    • asynq queue: Manage queues with subcommands: ls, inspect, history, rm, pause, unpause.
    • asynq task: Manage tasks with subcommands: ls, cancel, delete, archive, run, deleteall, archiveall, runall.
    • asynq server: Manage servers with subcommands: ls.
  3. Define tasks and handlers

    master

    To use Asynq, you must define two things: how to create a task and how to process it.

    1. Task Creation

    A task consists of a type (a string) and a payload (byte slice). It is best practice to wrap task creation in a helper function that marshals your data into JSON.

    2. Task Handling

    Handlers process the tasks. You can implement them in two ways:

    • asynq.HandlerFunc: A simple function with the signature func(context.Context, *asynq.Task) error.
    • asynq.Handler interface: A struct that implements the ProcessTask(context.Context, *asynq.Task) error method. This is useful when your handler needs to maintain state or dependencies.
    // Task definition example
    const TypeEmailDelivery = "email:deliver"
    
    type EmailDeliveryPayload struct {
        UserID     int
        TemplateID string
    }
    
    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
    }
    
    // HandlerFunc example
    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)
        }
        // ... logic
        return nil
    }
    
    // Handler interface example
    type ImageProcessor struct {}
    
    func (processor *ImageProcessor) ProcessTask(ctx context.Context, t *asynq.Task) error {
        // ... logic
        return nil
    }
  4. Install the Asynq library

    master

    To use Asynq in your Go project, ensure you have Go installed (the last two versions are supported) and a Redis server running (version 4.0 or higher is required).

    Initialize your module and install the library using go get:

    go mod init github.com/your/repo
    go get -u github.com/hibiken/asynq
  5. Understand Task State Visualizations

    master

    The Asynq dashboard uses color-coded bar graphs and text labels to represent the different states of tasks within a queue. This helps you quickly identify where bottlenecks or failures are occurring.

    Task States and Colors

    StateColorDescription
    ActiveBlueTasks currently being processed by a worker
    PendingGreenTasks waiting to be processed
    AggregatingLight GreenTasks that are part of a group being aggregated
    ScheduledYellowTasks scheduled to run at a future time
    RetryPinkTasks that failed and are waiting for a retry
    ArchivedPurpleTasks that have been moved to the archive
    CompletedDark GreenTasks that have finished successfully
  6. Aggregate tasks with GroupAggregator

    master

    The GroupAggregator interface allows the server to collect multiple incoming tasks belonging to the same group and combine them into a single task before passing them to the Handler. This is useful for batch processing.

    To enable this, you must provide a GroupAggregator in the Config and configure:

    • GroupGracePeriod: How long to wait for incoming tasks in a group.
    • GroupMaxDelay: The maximum time to wait for aggregation.
    • GroupMaxSize: The maximum number of tasks to aggregate into one.
    type myAggregator struct{}
    
    func (a *myAggregator) Aggregate(group string, tasks []*asynq.Task) *asynq.Task {
    	// logic to combine tasks
    	return asynq.NewTask("aggregated_task_type", combinedPayload)
    }
    
    cfg := asynq.Config{
    	GroupAggregator: &myAggregator{},
    	GroupGracePeriod: 1 * time.Minute,
    	GroupMaxSize:     10,
    }
  7. Understand TaskInfo and TaskState

    master

    A TaskInfo struct provides a comprehensive snapshot of a task's metadata and current status. This is typically used for monitoring or inspecting tasks in a queue.

    Task States

    • TaskStateActive: Currently being processed by a handler.
    • TaskStatePending: Ready to be processed.
    • TaskStateScheduled: Scheduled for a future time.
    • TaskStateRetry: Previously failed and scheduled for retry.
    • TaskStateArchived: Archived for inspection.
    • TaskStateCompleted: Processed successfully (retained until TTL expires).
    • TaskStateAggregating: Waiting in a group to be aggregated.
  8. Configure Asynq CLI with a config file

    master

    To avoid passing connection flags every time, you can use a configuration file. By default, the CLI looks for a file at $HOME/.asynq.yaml or $HOME/.asynq.json. You can override this location using the --config flag.

    uri: 127.0.0.1:6379
    db: 2
    password: mypassword
  9. Manage periodic tasks with PeriodicTaskManager

    master

    The PeriodicTaskManager is used to schedule and run tasks at regular intervals by synchronizing with a PeriodicTaskConfigProvider. It manages a background goroutine that periodically calls the provider to fetch the latest task configurations and updates the underlying Scheduler by adding new tasks or removing those no longer present in the configuration.

    To use it, you must provide a PeriodicTaskConfigProvider implementation and a Redis connection (either via RedisConnOpt or a RedisUniversalClient).

    // Example setup (conceptual)
    manager, err := asynq.NewPeriodicTaskManager(asynq.PeriodicTaskManagerOpts{
        PeriodicTaskConfigProvider: myConfigProvider, // Implements PeriodicTaskConfigProvider
        RedisConnOpt:               asynq.RedisConnOpt{Addr: "localhost:6379"},
        SyncInterval:               1 * time.Minute,
    })
    
    if err != nil {
        log.Fatal(err)
    }
    
    // Run will start the manager and block until an OS signal is received
    if err := manager.Run(); err != nil {
        log.Fatal(err)
    }
  10. Use ServeMux for task routing

    master

    A ServeMux is a multiplexer used to route asynchronous tasks to their appropriate handlers based on the task's type name. It supports pattern matching where longer, more specific patterns take precedence over shorter ones.

    Pattern Matching Rules

    • Exact Match: If a task type matches a registered pattern exactly, that handler is used.
    • Prefix Match: If no exact match is found, the ServeMux searches for the longest pattern that is a prefix of the task type.
    • Precedence: Longer patterns win. For example, if you have handlers for images and images:thumbnails, a task of type images:thumbnails:small will be routed to the images:thumbnails handler.
    • Fallback: If no pattern matches, the ServeMux returns a NotFoundHandler, which results in an ErrHandlerNotFound error.
    // Example of pattern precedence
    mux := asynq.NewServeMux()
    
    mux.Handle("images", imagesHandler)
    // This handler will be chosen for "images:thumbnails" because it is more specific
    mux.Handle("images:thumbnails", thumbnailHandler)
    
    // Task type "images:thumbnails:large" matches "images:thumbnails"
    // Task type "images:archive" matches "images"