用RabbitMQ和golang实现一个异步任务系统,你会不会?
在使用 RabbitMQ 和 Go 语言实现一个异步任务系统时,你可以将任务分配给生产者,将任务的处理交给消费者,这样消费者可以异步处理这些任务。
RabbitMQ 是一个强大的消息队列系统,它允许多个生产者和多个消费者进行异步通信,这使得它成为构建异步任务系统的理想选择。
系统架构概述
-
生产者 (Producer):生产者是任务的发送方。它会将任务发送到 RabbitMQ 中的一个队列。
-
消费者 (Consumer):消费者从 RabbitMQ 队列中获取任务,并处理这些任务。
-
RabbitMQ:它充当消息中间件,负责存储和分发消息(任务)。
步骤概述:
-
安装 RabbitMQ。
-
使用 Go 的
amqp
库与 RabbitMQ 交互。 -
实现生产者,将任务发送到队列中。
-
实现消费者,从队列中获取任务并处理。
》》》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. 运行步骤
-
启动 RabbitMQ Docker 容器(如果还没有启动)。
-
运行消费者(Consumer)程序:
go run consumer.go
-
运行生产者(Producer)程序:
go run producer.go
消费者会自动获取生产者发布的任务,并处理这些任务。在这个例子中,消费者每次处理一条消息后会等待 2 秒钟,模拟任务处理的过程。
6. 扩展
这个基础结构可以进一步扩展来支持更多功能:
-
任务重试机制:在消费者无法处理消息时,可以重新放回队列或者进行错误处理。
-
多个消费者:可以启动多个消费者,处理并行任务。
-
任务优先级:可以基于消息属性设置不同的优先级。
-
延迟任务:可以使用 RabbitMQ 插件实现延迟队列,调度未来的任务。
通过 RabbitMQ 和 Go 的结合,你可以轻松构建一个高效的异步任务系统,并根据需求进行扩展。