用RabbitMQ和golang实现一个异步任务系统,你会不会?

在使用 RabbitMQ 和 Go 语言实现一个异步任务系统时,你可以将任务分配给生产者,将任务的处理交给消费者,这样消费者可以异步处理这些任务。

RabbitMQ 是一个强大的消息队列系统,它允许多个生产者和多个消费者进行异步通信,这使得它成为构建异步任务系统的理想选择。

系统架构概述

  • 生产者 (Producer):生产者是任务的发送方。它会将任务发送到 RabbitMQ 中的一个队列。

  • 消费者 (Consumer):消费者从 RabbitMQ 队列中获取任务,并处理这些任务。

  • RabbitMQ:它充当消息中间件,负责存储和分发消息(任务)。

步骤概述:

  1. 安装 RabbitMQ。

  2. 使用 Go 的 amqp 库与 RabbitMQ 交互。

  3. 实现生产者,将任务发送到队列中。

  4. 实现消费者,从队列中获取任务并处理。

 

》》》GoLand永久账号 免费领取《《《

 

图片

 

1. 安装 RabbitMQ

RabbitMQ 可以通过 Docker 快速安装:

docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management

RabbitMQ 将运行在 localhost:5672 上,管理控制台运行在 localhost:15672。默认登录用户名和密码为 guest/guest

2. 安装 Go 的 amqp 库

首先,安装用于与 RabbitMQ 进行交互的 Go 包 github.com/streadway/amqp

go get github.com/streadway/amqp

3. 生产者实现(Producer)

生产者将任务发送到 RabbitMQ 的队列中。

package main

import (
"fmt"
"log"
"github.com/streadway/amqp"
)

// 连接 RabbitMQ
func connectRabbitMQ() (*amqp.Connection, error) {
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
if err != nil {
return nil, fmt.Errorf("failed to connect to RabbitMQ: %w", err)
}
return conn, nil
}

func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}

func main() {
conn, err := connectRabbitMQ()
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()

// 创建一个信道
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()

// 声明队列
q, err := ch.QueueDeclare(
"task_queue", // 队列名称
true, // 持久化
false, // 是否自动删除
false, // 是否排他
false, // 是否阻塞
nil, // 额外参数
)
failOnError(err, "Failed to declare a queue")

// 要发送的消息
body := "This is a task"
err = ch.Publish(
"", // 交换机
q.Name, // 队列名称
false, // 如果没有路由到队列,是否返回消息
false, // 如果消费者没有接收,是否返回消息
amqp.Publishing{
DeliveryMode: amqp.Persistent, // 持久化消息
ContentType: "text/plain",
Body: []byte(body),
})
failOnError(err, "Failed to publish a message")
log.Printf(" [x] Sent %s", body)
}

这个生产者会将消息 This is a task 发送到名为 task_queue 的 RabbitMQ 队列中。

 

4. 消费者实现(Consumer)

消费者将从队列中异步获取消息,并处理这些消息。

package main

import (
"bytes"
"log"
"time"
"github.com/streadway/amqp"
)

// 错误处理函数
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}

// 模拟耗时任务
func worker(body string) {
log.Printf("Processing task: %s", body)
// 模拟耗时任务
time.Sleep(2 * time.Second)
log.Printf("Task finished: %s", body)
}

func main() {
// 连接 RabbitMQ
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()

// 打开信道
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()

// 声明队列
q, err := ch.QueueDeclare(
"task_queue", // 队列名称
true, // 持久化
false, // 是否自动删除
false, // 是否排他
false, // 是否阻塞
nil, // 额外参数
)
failOnError(err, "Failed to declare a queue")

// 消费者从队列中获取消息
msgs, err := ch.Consume(
q.Name, // 队列名称
"", // 消费者标识
false, // 自动应答
false, // 是否排他
false, // 非本地
false, // 阻塞
nil, // 额外参数
)
failOnError(err, "Failed to register a consumer")

// 启动协程处理消息
forever := make(chan bool)
go func() {
for d := range msgs {
// 模拟任务处理
log.Printf("Received a message: %s", d.Body)
worker(string(d.Body))

// 手动应答消息已处理
d.Ack(false)
}
}()

log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
<-forever
}

5. 运行步骤

  1. 启动 RabbitMQ Docker 容器(如果还没有启动)。

  2. 运行消费者(Consumer)程序:

    go run consumer.go
  3. 运行生产者(Producer)程序:

    go run producer.go

消费者会自动获取生产者发布的任务,并处理这些任务。在这个例子中,消费者每次处理一条消息后会等待 2 秒钟,模拟任务处理的过程。

6. 扩展

这个基础结构可以进一步扩展来支持更多功能:

  • 任务重试机制:在消费者无法处理消息时,可以重新放回队列或者进行错误处理。

  • 多个消费者:可以启动多个消费者,处理并行任务。

  • 任务优先级:可以基于消息属性设置不同的优先级。

  • 延迟任务:可以使用 RabbitMQ 插件实现延迟队列,调度未来的任务。

通过 RabbitMQ 和 Go 的结合,你可以轻松构建一个高效的异步任务系统,并根据需求进行扩展。

posted @ 2024-10-22 10:09  技术颜良  阅读(16)  评论(0编辑  收藏  举报