我们一起来学RabbitMQ 二:RabbiMQ 的 6 种模式的基本应用
嗨,大家好,我是小魔童哪吒,咱们从今天开始进入开源组件的学习,一边学习一边总结一边分享
文章提纲如下:
- RabbitMQ 成员组成
- RabbitMQ 的六种工作模式编码
RabbitMQ 成员组成
- 生产者 producer
- 消费者 consumer
- 交换机 exchange
用于接受、分配消息
- 消息 message
- 队列 queue
用于存储生产者的消息
- 信道 channel AMQP
消息推送使用的通道
- 连接 connections
生成者或者消费者与Rabbit 建立的TCP 连接
- 路由键 routingKey
用于把生成者的数据分配到交换器上
- 绑定键 BindingKey
用于把交换器的消息绑定到队列上
- 连接管理器 ConnectionFactory
应用程序与 Rabbit 之间建立连接的管理器,程序代码中使用
RabbitMQ 的六种工作模式编码
single 模式
- 消息产生者将消息放入队列
- 消息的消费者监听消息队列,如果队列中有消息就消费掉
目录如下:
.
├── consumer.go
├── go.mod
├── go.sum
├── main.go
└── xmtmq
└── xmtmq.go
实际编码如下:
每种模式的编码思路如下:
生产者 / 消费者
- 连接 RabbitMQ 的 server
- 初始化连接 connection
- 初始化通道 channel
- 初始化交换机 exchange
- 初始化队列 queue
- 使用路由key,绑定队列 bind , key
- 生产消息 / 消费消息 produce , consume
消息xmtmq.go
package xmtmq
import (
"github.com/streadway/amqp"
"log"
)
// single 模式
// 定义 RabbitMQ 的数据结构
// go get github.com/streadway/amqp
type RabbitMQ struct {
conn *amqp.Connection // 连接
channel *amqp.Channel // 通道
QueueName string // 队列名
Exchange string // 交换机
Key string // 路由键
MQUrl string // MQ的虚拟机地址
}
// New 一个 RabbitMQ
func NewRabbitMQ(rbt *RabbitMQ) {
if rbt == nil || rbt.QueueName == "" || rbt.MQUrl == "" {
log.Panic("please check QueueName,Exchange,MQUrl ...")
}
conn, err := amqp.Dial(rbt.MQUrl)
if err != nil {
log.Panicf("amqp.Dial error : %v", err)
}
rbt.conn = conn
channel, err := rbt.conn.Channel()
if err != nil {
log.Panicf("rbt.conn.Channel error : %v", err)
}
rbt.channel = channel
}
func RabbitMQFree(rbt *RabbitMQ){
if rbt == nil{
log.Printf("rbt is nil,free failed")
return
}
rbt.channel.Close()
rbt.conn.Close()
}
func (rbt *RabbitMQ) Init() {
// 申请队列
_, err := rbt.channel.QueueDeclare(
rbt.QueueName, // 队列名
true, // 是否持久化
false, // 是否自动删除
false, // 是否排他
false, // 是否阻塞
nil, // 其他参数
)
if err != nil {
log.Printf("rbt.channel.QueueDeclare error : %v", err)
return
}
}
// 生产消息
func (rbt *RabbitMQ) Produce(data []byte) {
// 向队列中加入数据
err := rbt.channel.Publish(
rbt.Exchange, // 交换机
rbt.QueueName, // 队列名
false, // 若为true,根据自身exchange类型和routekey规则无法找到符合条件的队列会把消息返还给发送者
false, // 若为true,当exchange发送消息到队列后发现队列上没有消费者,则会把消息返还给发送者
amqp.Publishing{
ContentType: "text/plain",
Body: data,
},
)
if err != nil {
log.Printf("rbt.channel.Publish error : %v", err)
return
}
return
}
// 消费消息
func (rbt *RabbitMQ) Consume() {
// 消费数据
msg, err := rbt.channel.Consume(
rbt.QueueName, // 队列名
"xmt", // 消费者的名字
true, // 是否自动应答
false, // 是否排他
false, // 若为true,表示 不能将同一个Conenction中生产者发送的消息传递给这个Connection中 的消费者
false, // 是否阻塞
nil, // 其他属性
)
if err != nil {
log.Printf("rbt.channel.Consume error : %v", err)
return
}
for data := range msg {
log.Printf("received data is %v", string(data.Body))
}
}
main.go
package main
import (
"fmt"
"log"
"time"
"xmt/xmtmq"
)
/*
RabbimtMQ single 模式 案例
应用场景:简单消息队列的使用,一个生产者一个消费者
生产消息
*/
func main() {
// 设置日志
log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)
rbt := &xmtmq.RabbitMQ{
QueueName: "xmtqueue",
MQUrl: "amqp://guest:guest@127.0.0.1:5672/xmtmq",
}
xmtmq.NewRabbitMQ(rbt)
var index = 0
for {
// 生产消息
rbt.Produce([]byte(fmt.Sprintf("hello wolrd %d ", index)))
log.Println("发送成功 ", index)
index++
time.Sleep(1 * time.Second)
}
}
consumer.go
package main
import (
"log"
"xmt/xmtmq"
)
func main() {
log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)
rbt := &xmtmq.RabbitMQ{
QueueName: "xmtqueue",
MQUrl: "amqp://guest:guest@127.0.0.1:5672/xmtmq",
}
xmtmq.NewRabbitMQ(rbt)
rbt.Consume()
}
运行的时候,打开2个终端
终端1:go run main.go
终端2:go run consumer.go
work 模式
多个消费端消费同一个队列中的消息,队列采用轮询的方式将消息是平均发送给消费者,此处的资源是竞争关系
当生产者生产消息的速度大于消费者消费的速度,