golang RabbitMQ教程一Hello World
介绍#
RabbitMQ是消息中间件:它接受并转发消息。
您可以将其视为邮局系统:将要发送的邮件放在邮箱中时,
可以确保邮递员最终将邮件传递给收件人。
以此类推,RabbitMQ是一个邮箱,一个邮局和一个邮递员。
RabbitMQ与邮局之间的主要区别在于,
它不处理纸张,而是接收,存储和转发数据消息的二进制数据。
以下是RabbitMQ和消息发送的术语
- Producer:生产者。负责生产消息。
- Queue:队列。负责存储消息。队列在RabbitMQ中充当邮箱的角色,消息传递到RabbitMQ中,只能存储在队列中。队列受主机内存和磁盘大小的约束。本质是一个很大的消息缓冲区。
许多生产者可以将消息发送到一个队列,许多消费者可以尝试从一个队列接收数据。 - Consumer:消费者。负责处理消息。
另外:
-
Connect:连接。生产者和RabbitMQ服务之间建立的TCP连接。
-
Channel:信道,一条连接可包含多条信道,不同信道之间通信互不干扰。考虑下多线程应用场景,每个线程对应一条信道,而不是对应一条连接,这样可以提高性能。
-
body:消息主体,要传递的数据。
-
exchange:交换器,负责把消息转发到对应的队列。交换器本身没有缓存消息的功能,消息是在队列中缓存的,如果队列不存在,则交换器会直接丢弃消息。常用的有四种类型的交换器:direct、fanout、topic、headers。不同类型的交换器有不同的交换规则,交换器会根据交换规则把消息转发到对应的队列。
-
exchangeName:交换器名称,每个交换器对应一个名称,发送消息时会附带交换器名称,根据交换器名称选择对应的交换器。
-
BandingKey:绑定键,一个队列可以有一个到多个绑定键,通过绑定操作可以绑定交换器和队列,交换器会根据绑定键的名称找到对应的队列。
-
RotingKey:路由键,发送消息时,需要附带一条路由键,交换器会对路由键和绑定键进行匹配,如果匹配成功,则消息会转发到绑定键对应的队列中。
简而言之就是:
1. 生产者指定路由Key和交换器的名字发送给RabbitMQ服务
2. 指定名字的交换器根据路由key去找到绑定的队列
3. 将消息放入队列当中
golang语言实现Hello World#
我们将用Go编写两个小程序。 发送单个消息的生产者和接收消息并打印出来的消费者。 我们将介绍Go RabbitMQ API中的一些细节,仅着眼于此非常简单的事情。 这是消息传递的“ Hello World”。
在下图中,“ P”是我们的生产者,“ C”是我们的消费者。 中间的框是一个队列-RabbitMQ代表使用者保留的消息缓冲区。
Go RabbitMQ客户端库
RabbitMQ使用多种协议。 本教程使用AMQP 0-9-1,这是一种开放的通用消息传递协议。 RabbitMQ有许多不同语言的客户。 在本教程中,我们将使用Go amqp客户端。
go get github.com/streadway/amqp
生产者发送数据到队列
#send.go 生产者,发送消息到消息队列中
package main
import (
"github.com/streadway/amqp"
"log"
)
func main(){
// 连接RabbitMQ服务器
conn, err := amqp.Dial("amqp://guest:guest@127.0.0.1:5672/")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
// 创建一个channel
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
// 声明一个队列
q, err := ch.QueueDeclare(
"hello", // 队列名称
false, // 是否持久化
false, // 是否自动删除
false, // 是否独立
false,nil,
)
failOnError(err, "Failed to declare a queue")
// 发送消息到队列中
body := "Hello World!"
err = ch.Publish(
"", // exchange
q.Name, // routing key
false, // mandatory
false, // immediate
amqp.Publishing {
ContentType: "text/plain",
Body: []byte(body),
})
failOnError(err, "Failed to publish a message")
fmt.Println("send message success\n"
}
// 帮助函数检测每一个amqp调用
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
package main
import (
"github.com/streadway/amqp"
"log"
)
func main(){
// 连接RabbitMQ服务器
conn, err := amqp.Dial("amqp://admin:admin@47.97.215.189:5672/admin")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
// 创建一个channel
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
// 监听队列
q, err := ch.QueueDeclare(
"hello", // 队列名称
false, // 是否持久化
false, // 是否自动删除
false, // 是否独立
false,nil,
)
failOnError(err, "Failed to declare a queue")
// 消费队列
msgs, err := ch.Consume(
q.Name, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
failOnError(err, "Failed to register a consumer")
// 申明一个goroutine,一遍程序始终监听
forever := make(chan bool)
go func() {
for d := range msgs {
log.Printf("Received a message: %s", d.Body)
}
}()
log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
<-forever
}
// 帮助函数检测每一个amqp调用
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
代码注释:
failOnError
是我们编写一个通用的辅助方法,用来检查每一步amqp调用的结果。另外,在Go语言中经常需要使用if语句来检查操作结果,为了避免在代码中到处散落if(err != nil)语句,可以使用下列方法:
func failOnError(err error, msg string){
if err != nil {
log.Fatalf("%s:%s", msg, err)
}
}
连接RabbitMQ服务器:
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
上面代码会建立一个socket连接,处理一些协议转换及版本对接和登录授权的问题。建立连接之后,我们需要创建一个通道channel,之后我们的大多数API操作都是围绕通道来实现的:
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
最后,我们需要定义一个队列用来存储、转发消息,然后我们的sender只需要将消息发送到这个队列中,就完成了消息的publish操作:
q, err := ch.QueueDeclare(
"hello", //name
false, //durable
false, //delete when unused
false, //exclusive
false, //no wait
nil, //arguments
)
failOnError(err, "Failed to declare q queue")
body := "Hello"
err = ch.Publish(
"", //exchange
q.Name, // routing key
false, //mandatory
false, //immediate
amqp.Publishing{
ContentType: "text/plain",
Body : []byte(body),
}
)
failOnError(err, "Failed to publish a message")
定义队列操作具备幂等性,也就是说多次重复定义,相同名称的队列只会创建一个。发送给队列的内容是byte数组,将任意格式数据转换成byte数组是一件很简单的事情,因此对于任何格式的数据,要将它发送到队列中是很容易的。
以上完成了发送消息的程序。不同于消息发送程序只需要将单一的消息推送至队列后推出,消息接收者需要保持一个监听程序从队列中不断的接收消息。
实现从RabbitMQ队列中接收消息的消费者程序与生产者一样,打开连接并创建通道,注意这里的参数必须与send中的queue name相一致,这样才能实现发送/接受的配对。
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672")
failOnError(err, "Failed to connect to server")
defer conn.Close();
ch, err := conn.Channel()
failOnError(err, "Failed to connect to channel")
defer ch.Close()
q, err := ch.QueueDeclare(
"hello", //name
false, //durable
false, //delete when usused
false, // exclusive
false, //no-wait
nil, // arguments
)
failOnError(err, "Failed to declare a queue")
一般来说,接收消息的程序会先于发送者运行,因此在这里我们先定义一个queue,确保后面发送者连接到这个queue时,当前接收消息程序以运行。
接下来,需要RabbitMQ服务器让它将消息分发到我们的消费者程序中,消息转发操作是异步执行的,这里使用goroutine来完成从队列中的读取消息操作:
msgs, err := ch.Consume(
q.Name, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // arguments
)
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)
}
}()
log.Printf(" [*] Waiting for messages, To exit press CTRL+C")
<-forever
Running#
首先,在命令行中先运行消费者:
go run receive.go
当前程序会一直监听RabbitMQ的队列消息,一旦接收到消息后会直接打印出来,使用Ctrl+C可以终止程序;
接着,在另一个命令终端中运行生产者:
go run send.go
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?