嗨,大家好,我是小魔童哪吒,我們們從今天開始進入開源元件的學習,一邊學習一邊總結一邊分享
文章提綱如下:
- 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 模式
多個消費端消費同一個佇列中的訊息,佇列採用輪詢的方式將訊息是平均傳送給消費者,此處的資源是競爭關係
當生產者生產訊息的速度大於消費者消費的速度,就要考慮用 work 工作模式,這樣能提高處理速度提高負載
work 模式與 single 模式類似, 只是work 模式比 single 模式多了一些消費者
基於single
模式,開一個終端3 : go run consumer.go
publish / subscribe 模式
publish / subscribe
釋出訂閱模式 , 相對於Work queues模式多了一個交換機,此處的資源是共享的
用於場景
- 郵件群發
- 群聊天
- 廣播(廣告等)
目錄和上述編碼保持一致:
xmtmq.go
開始用到交換機 exchange ,fanout 型別
生產端先把訊息傳送到交換機,再由交換機把訊息傳送到繫結的佇列中,每個繫結的佇列都能收到由生產端傳送的訊息
package xmtmq
import (
"github.com/streadway/amqp"
"log"
)
// publish 模式
// 定義 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.Exchange == "" || rbt.MQUrl == "" {
log.Panic("please check 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() {
// 1、建立交換機
err := rbt.channel.ExchangeDeclare(
rbt.Exchange, // 交換機
amqp.ExchangeFanout, // 交換機型別
true, // 是否持久化
false, //是否自動刪除
false, //true表示這個exchange不可以被client用來推送訊息,僅用來進行exchange和exchange之間的繫結
false, // 是否阻塞
nil, // 其他屬性
)
if err != nil {
log.Printf("rbt.channel.ExchangeDeclare error : %v", err)
return
}
}
// 生產訊息 publish
func (rbt *RabbitMQ) PublishMsg(data []byte) {
// 1、向佇列中加入資料
err := rbt.channel.Publish(
rbt.Exchange, // 交換機
"", // 佇列名
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) SubscribeMsg() {
// 1、建立佇列
q, err := rbt.channel.QueueDeclare(
"", // 此處我們傳入的是空,則是隨機產生佇列的名稱
true,
false,
false,
false,
nil,
)
if err != nil {
log.Printf("rbt.channel.QueueDeclare error : %v", err)
return
}
// 2、繫結佇列
err = rbt.channel.QueueBind(
q.Name, // 佇列名字
"", // 在publish模式下,這裡key 為空
rbt.Exchange, // 交換機名稱
false, // 是否阻塞
nil, // 其他屬性
)
if err != nil {
log.Printf("rbt.channel.QueueBind error : %v", err)
return
}
// 3、消費資料
msg, err := rbt.channel.Consume(
q.Name, // 佇列名
"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 publish 模式 案例
應用場景:郵件群發,群聊天,廣播(廣告)
生產訊息
*/
func main() {
log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)
rbt := &xmtmq.RabbitMQ{
Exchange: "xmtPubEx",
MQUrl: "amqp://guest:guest@127.0.0.1:5672/xmtmq",
}
xmtmq.NewRabbitMQ(rbt)
rbt.Init()
var index = 0
for {
rbt.PublishMsg([]byte(fmt.Sprintf("hello wolrd %d ", index)))
log.Println("傳送成功 ", index)
index++
time.Sleep(1 * time.Second)
}
xmtmq.RabbitMQFree(rbt)
}
consumer.go
package main
import (
"log"
"xmt/xmtmq"
)
func main() {
log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)
rbt := &xmtmq.RabbitMQ{
Exchange: "xmtPubEx",
MQUrl: "amqp://guest:guest@127.0.0.1:5672/xmtmq",
}
xmtmq.NewRabbitMQ(rbt)
rbt.SubscribeMsg()
xmtmq.RabbitMQFree(rbt)
}
執行的操作和上述保持一致
終端1:go run main.go
終端2:go run consumer.go
終端3:go run consumer.go
效果和上述single
模式和 work
模式的明顯區別是:釋出訂閱模式的案例,生產者生產的訊息,對應的消費者消費其生產的內容
routing 模式
訊息生產者將訊息傳送給交換機按照路由判斷,路由是字串 當前產生的訊息攜帶路由字元(物件的方法),交換機根據路由的key,只能匹配上路由key對應的訊息佇列,對應的消費者才能消費訊息
應用場景:從系統的程式碼邏輯中獲取對應的功能字串,將訊息任務扔到對應的佇列中業務場景,例如處理錯誤,處理特定訊息等
生產者處理流程:
宣告佇列並宣告交換機 -> 建立連線 -> 建立通道 -> 通道宣告交換機 -> 通道宣告佇列 -> 通過通道使佇列繫結到交換機並指定該佇列的routingkey(萬用字元) -> 制定訊息 -> 傳送訊息並指定routingkey(萬用字元)
消費者處理流程:
宣告佇列並宣告交換機 -> 建立連線 -> 建立通道 -> 通道宣告交換機 -> 通道宣告佇列 -> 通過通道使佇列繫結到交換機並指定routingkey(萬用字元) -> 重寫訊息消費方法 -> 執行訊息方法
目錄結構如下:
.
├── consumer2.go
├── consumer.go
├── go.mod
├── go.sum
├── main.go
└── xmtmq
└── xmtmq.go
xmtmq.go
用到交換機 為
direct
型別用到路由鍵
package xmtmq
import (
"github.com/streadway/amqp"
"log"
)
// routing 模式
// 定義 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.Exchange == "" || rbt.QueueName == "" || rbt.Key == "" || rbt.MQUrl == "" {
log.Panic("please check Exchange,,QueueName,Key,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() {
// 1、建立交換機
err := rbt.channel.ExchangeDeclare(
rbt.Exchange, // 交換機
amqp.ExchangeDirect, // 交換機型別
true, // 是否持久化
false, //是否自動刪除
false, //true表示這個exchange不可以被client用來推送訊息,僅用來進行exchange和exchange之間的繫結
false, // 是否阻塞
nil, // 其他屬性
)
if err != nil {
log.Printf("rbt.channel.ExchangeDeclare error : %v", err)
return
}
// 2、建立佇列
_, err = rbt.channel.QueueDeclare(
rbt.QueueName, // 此處我們傳入的是空,則是隨機產生佇列的名稱
true,
false,
false,
false,
nil,
)
if err != nil {
log.Printf("rbt.channel.QueueDeclare error : %v", err)
return
}
// 3、繫結佇列
err = rbt.channel.QueueBind(
rbt.QueueName, // 佇列名字
rbt.Key, // routing,這裡key 需要填
rbt.Exchange, // 交換機名稱
false, // 是否阻塞
nil, // 其他屬性
)
if err != nil {
log.Printf("rbt.channel.QueueBind error : %v", err)
return
}
}
// 生產訊息 publish
func (rbt *RabbitMQ) ProduceRouting(data []byte) {
// 1、向佇列中加入資料
err := rbt.channel.Publish(
rbt.Exchange, // 交換機
rbt.Key, // key
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) ConsumeRoutingMsg() {
// 4、消費資料
msg, err := rbt.channel.Consume(
rbt.QueueName, // 佇列名
"", // 消費者的名字
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 routing 模式 案例
應用場景:從系統的程式碼邏輯中獲取對應的功能字串,將訊息任務扔到對應的佇列中業務場景,例如處理錯誤,處理特定訊息等
生產訊息
*/
func main() {
log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)
rbt1 := &xmtmq.RabbitMQ{
Exchange: "xmtPubEx2",
Key: "xmt1",
QueueName: "Routingqueuexmt1",
MQUrl: "amqp://guest:guest@127.0.0.1:5672/xmtmq",
}
xmtmq.NewRabbitMQ(rbt1)
rbt1.Init()
rbt2 := &xmtmq.RabbitMQ{
Exchange: "xmtPubEx2",
Key: "xmt2",
QueueName: "Routingqueuexmt2",
MQUrl: "amqp://guest:guest@127.0.0.1:5672/xmtmq",
}
xmtmq.NewRabbitMQ(rbt2)
rbt2.Init()
var index = 0
for {
rbt1.ProduceRouting([]byte(fmt.Sprintf("hello wolrd xmt1 %d ", index)))
log.Println("傳送成功xmt1 ", index)
rbt2.ProduceRouting([]byte(fmt.Sprintf("hello wolrd xmt2 %d ", index)))
log.Println("傳送成功xmt2 ", index)
index++
time.Sleep(1 * time.Second)
}
xmtmq.RabbitMQFree(rbt1)
xmtmq.RabbitMQFree(rbt2)
}
consumer.go
package main
import (
"log"
"xmt/xmtmq"
)
func main() {
log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)
rbt := &xmtmq.RabbitMQ{
Exchange: "xmtPubEx2",
Key: "xmt1",
QueueName: "Routingqueuexmt1",
MQUrl: "amqp://guest:guest@127.0.0.1:5672/xmtmq",
}
xmtmq.NewRabbitMQ(rbt)
rbt.ConsumeRoutingMsg()
xmtmq.RabbitMQFree(rbt)
}
consumer2.go
package main
import (
"log"
"xmt/xmtmq"
)
func main() {
log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)
rbt := &xmtmq.RabbitMQ{
Exchange: "xmtPubEx2",
Key: "xmt2",
QueueName: "Routingqueuexmt2",
MQUrl: "amqp://guest:guest@127.0.0.1:5672/xmtmq",
}
xmtmq.NewRabbitMQ(rbt)
rbt.ConsumeRoutingMsg()
xmtmq.RabbitMQFree(rbt)
}
topic 模式
話題模式,一個訊息被多個消費者獲取,訊息的目標 queue 可用 BindingKey
的萬用字元
Topics 模式實際上是路由模式的一種
他倆的最大的區別是 : Topics 模式傳送訊息和消費訊息的時候是通過萬用字元去進行匹配的
- *號代表可以同通配一個單詞
- #號代表可以通配零個或多個單詞
編碼的案例與上述 routing 模式保持一直,只是 exchange 為 topic
型別
如下是上述幾種模式涉及到的交換機
和佇列
rpc 模式
RPC
遠端過程呼叫,客戶端遠端呼叫服務端的方法 ,使用 MQ
可以實現 RPC
的非同步呼叫
目錄結構為:
.
├── consumer.go
├── go.mod
├── go.sum
├── main.go
└── xmtmq
└── xmtmq.go
- 客戶端即是生產者也是消費者,向
RPC
請求佇列傳送RPC
呼叫訊息,同時監聽RPC
響應佇列 - 服務端監聽
RPC
請求佇列的訊息,收到訊息後執行服務端的方法,得到方法返回的結果 - 服務端將
RPC
方法 的結果傳送到RPC
響應佇列。 - 客戶端監聽
RPC
響應佇列,接收到RPC
呼叫結果
xmtmq.go
package xmtmq
import (
"github.com/streadway/amqp"
"log"
"math/rand"
)
// rpc 模式
// 定義 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) Produce(data []byte) {
// 申請佇列
q, err := rbt.channel.QueueDeclare(
rbt.QueueName, // 佇列名
true, // 是否持久化
false, // 是否自動刪除
false, // 是否排他
false, // 是否阻塞
nil, // 其他引數
)
if err != nil {
log.Printf("rbt.channel.QueueDeclare error : %v", err)
return
}
err = rbt.channel.Qos(1, 0, false)
if err != nil {
log.Printf("rbt.channel.Qos error : %v", err)
return
}
d, err := rbt.channel.Consume(
q.Name,
"",
false,
false,
false,
false,
nil)
if err != nil {
log.Printf("rbt.channel.Consume error : %v", err)
return
}
for msg := range d {
log.Println("received msg is ", string(msg.Body))
err := rbt.channel.Publish(
"",
msg.ReplyTo,
false,
false,
amqp.Publishing{
ContentType: "test/plain",
CorrelationId: msg.CorrelationId,
Body: data,
})
if err != nil {
log.Printf("rbt.channel.Publish error : %v", err)
return
}
msg.Ack(false)
log.Println("svr response ok ")
}
return
}
func randomString(l int) string {
bytes := make([]byte, l)
for i := 0; i < l; i++ {
bytes[i] = byte(rand.Intn(l))
}
return string(bytes)
}
// 消費訊息
func (rbt *RabbitMQ) Consume() {
// 申請佇列
q, err := rbt.channel.QueueDeclare(
"", // 佇列名
true, // 是否持久化
false, // 是否自動刪除
false, // 是否排他
false, // 是否阻塞
nil, // 其他引數
)
if err != nil {
log.Printf("rbt.channel.QueueDeclare error : %v", err)
return
}
// 消費資料
msg, err := rbt.channel.Consume(
q.Name, // 佇列名
"xmt", // 消費者的名字
true, // 是否自動應答
false, // 是否排他
false, // 若為true,表示 不能將同一個Conenction中生產者傳送的訊息傳遞給這個Connection中 的消費者
false, // 是否阻塞
nil, // 其他屬性
)
if err != nil {
log.Printf("rbt.channel.Consume error : %v", err)
return
}
id := randomString(32)
err = rbt.channel.Publish(
"",
rbt.QueueName,
false,
false,
amqp.Publishing{
ContentType: "test/plain",
CorrelationId: id,
ReplyTo: q.Name,
Body: []byte("321"),
})
if err != nil {
log.Printf("rbt.channel.Publish error : %v", err)
return
}
for data := range msg {
log.Printf("received data is %v", string(data.Body))
}
}
main.go
package main
import (
"fmt"
"log"
"xmt/xmtmq"
)
/*
RabbimtMQ rpc 模式 案例
應用場景:簡單訊息佇列的使用,一個生產者一個消費者
生產訊息
*/
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.Produce([]byte(fmt.Sprintf("hello wolrd")))
}
consumer.go
package main
import (
"log"
"math/rand"
"time"
"xmt/xmtmq"
)
func main() {
log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)
rand.Seed(time.Now().UTC().UnixNano())
rbt := &xmtmq.RabbitMQ{
QueueName: "xmtqueue",
MQUrl: "amqp://guest:guest@127.0.0.1:5672/xmtmq",
}
xmtmq.NewRabbitMQ(rbt)
rbt.Consume()
}
我們們先執行消費者,多執行幾個,可以看到我們們的佇列中已經有資料了,我們們執行的是2個消費者,因此此處是 2
再執行生產者,就能看到生產者將消費者傳送的訊息消費掉,並且通過 CorrelationId
找到對應消費者監聽的佇列,將資料傳送到佇列中
消費者監聽的佇列有資料了,消費者就取出來進行消費
總結
RabbitMQ
的六種工作模式:
- single 模式
- work 模式
- publish / subscribe 模式
- routing 模式
- topic 模式
- rpc 模式
參考資料:
歡迎點贊,關注,收藏
朋友們,你的支援和鼓勵,是我堅持分享,提高質量的動力
好了,本次就到這裡
技術是開放的,我們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。
我是小魔童哪吒,歡迎點贊關注收藏,下次見~
本作品採用《CC 協議》,轉載必須註明作者和本文連結