Let's learn RabbitMQ2: Basic applications of the six modes of RabbiMQ

Let's learn RabbitMQ2: Basic applications of the six modes of RabbiMQ ...
RabbitMQ Membership
Six working mode codes for RabbitMQ
summary
Welcome to Comment, Attention, Collection
Let's learn RabbitMQ2: Basic applications of the six modes of RabbiMQ

Hi, everyone, I'm a little devil. Let's start learning Open Source Components today and share them while learning.

The outline of the article is as follows:

  • RabbitMQ Membership
  • Six working mode codes for RabbitMQ

RabbitMQ Membership

  • Producer producer
  • consumer
  • Switch exchange

For accepting, distributing messages

  • Message message
  • queue

Messages for storing producers

  • channel AMQP

Channel used for message push

  • Connect connections

TCP Connection between Generator or Consumer and Rabbit

  • Routing key routingKey

Used to assign generator's data to switches

  • Binding Key

Messages used to bind switches to queues

  • Connection Manager ConnectionFactory

Manager for establishing a connection between an application and Rabbit, used in program code

Six working mode codes for RabbitMQ

single mode

  • Message Generator Queues Messages
  • The consumer of the message listens on the message queue and consumes it if there is a message in the queue

The catalog is as follows:

. ├── consumer.go ├── go.mod ├── go.sum ├── main.go └── xmtmq └── xmtmq.go

The actual codes are as follows:

The coding ideas for each pattern are as follows:

Producer/Consumer

  • Connect server to RabbitMQ
  • Initialize connection
  • Initialize channel
  • Initialize switch exchange
  • Initialize queue
  • Use routing key, bind queue bind, key
  • Production/consumption message produce, consume

Message xmtmq.go

package xmtmq import ( "github.com/streadway/amqp" "log" ) // single mode // Define the data structure of RabbitMQ // go get github.com/streadway/amqp type RabbitMQ struct { conn *amqp.Connection // Connect channel *amqp.Channel // passageway QueueName string // Queue name Exchange string // Switch Key string // Routing Key MQUrl string // Virtual Machine Address for MQ } // New a 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() { // Application queue _, err := rbt.channel.QueueDeclare( rbt.QueueName, // Queue name true, // Is it persistent false, // Whether to delete automatically false, // Is it exclusive false, // Is Blocking nil, // Other parameters ) if err != nil { log.Printf("rbt.channel.QueueDeclare error : %v", err) return } } // Production message func (rbt *RabbitMQ) Produce(data []byte) { // Add data to the queue err := rbt.channel.Publish( rbt.Exchange, // Switch rbt.QueueName, // Queue name false, // If true, failing to find a qualified queue based on its exchange type and routekey rule will return the message to the sender false, // If true, when exchange sends a message to the queue and finds that there are no consumers in the queue, it returns the message to the sender amqp.Publishing{ ContentType: "text/plain", Body: data, }, ) if err != nil { log.Printf("rbt.channel.Publish error : %v", err) return } return } // Consumer News func (rbt *RabbitMQ) Consume() { // Consumption data msg, err := rbt.channel.Consume( rbt.QueueName, // Queue name "xmt", // Name of the consumer true, // Whether to auto-answer false, // Is it exclusive false, // true means that messages from producers in the same Conenction cannot be delivered to consumers in this Connection false, // Is Blocking nil, // Other Properties ) 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 Model Case Scenario: Use of simple message queues, one producer and one consumer Production message */ func main() { // Set Log log.SetFlags(log.Llongfile | log.Ltime | log.Ldate) rbt := &xmtmq.RabbitMQ{ QueueName: "xmtqueue", MQUrl: "amqp://guest:[email protected]:5672/xmtmq", } xmtmq.NewRabbitMQ(rbt) var index = 0 for { // Production message rbt.Produce([]byte(fmt.Sprintf("hello wolrd %d ", index))) log.Println("Send Successfully ", 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:[email protected]:5672/xmtmq", } xmtmq.NewRabbitMQ(rbt) rbt.Consume() }

Open 2 terminals while running

Terminal 1:go run main.go

Terminal 2:go run consumer.go

work mode

Multiple consumers consume messages in the same queue. Queues poll to send messages to consumers on average, where resources are competitive

When a producer produces messages faster than a consumer consumes, consider using the work work mode, which can increase processing speed and load

The work mode is similar to the single mode except that the work mode has a few more consumers than the single mode

Start a terminal 3:go run consumer.go based on single mode

publish / subscribe mode

The publish / subscribe publish subscription mode has one more switch than the Work queues mode, where resources are shared

For scenes

  • Mass mailing
  • Group Chat
  • Broadcasting (Advertising, etc.)

The directory is consistent with the above codes:

xmtmq.go

Start using switch exchange, fanout type

The production side sends the message to the switch first, and then the switch sends the message to the bound queue. Each bound queue receives the message sent by the production side

package xmtmq import ( "github.com/streadway/amqp" "log" ) // publish mode // Define the data structure of RabbitMQ // go get github.com/streadway/amqp type RabbitMQ struct { conn *amqp.Connection // Connect channel *amqp.Channel // passageway QueueName string // Queue name Exchange string // Switch Key string // Routing Key MQUrl string // Virtual Machine Address for MQ } // New a 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. Create Switches err := rbt.channel.ExchangeDeclare( rbt.Exchange, // Switch amqp.ExchangeFanout, // Switch Type true, // Is it persistent false, //Whether to delete automatically false, //true means that this exchange cannot be used by client s to push messages, only to bind between exchange and exchange false, // Is Blocking nil, // Other Properties ) if err != nil { log.Printf("rbt.channel.ExchangeDeclare error : %v", err) return } } // Production message publish func (rbt *RabbitMQ) PublishMsg(data []byte) { // 1. Add data to the queue err := rbt.channel.Publish( rbt.Exchange, // Switch "", // Queue name false, // If true, failing to find a qualified queue based on its exchange type and routekey rule will return the message to the sender false, // If true, when exchange sends a message to the queue and finds that there are no consumers in the queue, it returns the message to the sender amqp.Publishing{ ContentType: "text/plain", Body: data, }, ) if err != nil { log.Printf("rbt.channel.Publish error : %v", err) return } return } // Consumer News func (rbt *RabbitMQ) SubscribeMsg() { // 1. Create Queues q, err := rbt.channel.QueueDeclare( "", // Here we pass in empty, then the name of the randomly generated queue true, false, false, false, nil, ) if err != nil { log.Printf("rbt.channel.QueueDeclare error : %v", err) return } // 2. Binding Queue err = rbt.channel.QueueBind( q.Name, // Queue name "", // In publish mode, the key is empty here rbt.Exchange, // Switch name false, // Is Blocking nil, // Other Properties ) if err != nil { log.Printf("rbt.channel.QueueBind error : %v", err) return } // 3. Consumption data msg, err := rbt.channel.Consume( q.Name, // Queue name "xmt", // Name of the consumer true, // Whether to auto-answer false, // Is it exclusive false, // true means that messages from producers in the same Conenction cannot be delivered to consumers in this Connection false, // Is Blocking nil, // Other Properties ) 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 Model Case Scenarios: mass mailing, group chat, broadcast (advertising) Production message */ func main() { log.SetFlags(log.Llongfile | log.Ltime | log.Ldate) rbt := &xmtmq.RabbitMQ{ Exchange: "xmtPubEx", MQUrl: "amqp://guest:[email protected]:5672/xmtmq", } xmtmq.NewRabbitMQ(rbt) rbt.Init() var index = 0 for { rbt.PublishMsg([]byte(fmt.Sprintf("hello wolrd %d ", index))) log.Println("Send Successfully ", 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:[email protected]:5672/xmtmq", } xmtmq.NewRabbitMQ(rbt) rbt.SubscribeMsg() xmtmq.RabbitMQFree(rbt) }

The actions performed are consistent with those described above

Terminal 1:go run main.go

Terminal 2:go run consumer.go

Terminal 3:go run consumer.go

The obvious differences between the effect and the single and work models above are: publishing subscription cases, producer-produced messages, and consumers consuming the content they produce.

routing mode

Message producers send messages to switches, which, according to routing, are messages currently generated by strings that carry routing characters (the method of objects). Switches, based on routing keys, can only match message queues corresponding to routing keys, so that consumers can consume messages.

Scenarios: Get the corresponding function string from the system's code logic, throw message tasks into the corresponding queue for business scenarios, such as handling errors, handling specific messages, and so on

Producer process:

Declare queues and switch -> Create Connection -> Create Channel -> Channel Claim Switch -> Channel declaration queue -> Bind a queue to a switch through a channel and specify the queue's routingkey(Wildcards) -> Make a message -> Send message and specify routingkey(Wildcards)

Consumer Processing:

Declare queues and switch -> Create Connection -> Create Channel -> Channel Claim Switch -> Channel declaration queue -> Bind queues to switches through channels and specify routingkey(Wildcards) -> Rewrite Message Consumption Method -> Execute Message Method

The directory structure is as follows:

. ├── consumer2.go ├── consumer.go ├── go.mod ├── go.sum ├── main.go └── xmtmq └── xmtmq.go

xmtmq.go

  • Switches used are of direct type

  • Use routing keys

package xmtmq import ( "github.com/streadway/amqp" "log" ) // routing mode // Define the data structure of RabbitMQ // go get github.com/streadway/amqp type RabbitMQ struct { conn *amqp.Connection // Connect channel *amqp.Channel // passageway QueueName string // Queue name Exchange string // Switch Key string // Routing Key MQUrl string // Virtual Machine Address for MQ } // New a 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. Create Switches err := rbt.channel.ExchangeDeclare( rbt.Exchange, // Switch amqp.ExchangeDirect, // Switch Type true, // Is it persistent false, //Whether to delete automatically false, //true means that this exchange cannot be used by client s to push messages, only to bind between exchange and exchange false, // Is Blocking nil, // Other Properties ) if err != nil { log.Printf("rbt.channel.ExchangeDeclare error : %v", err) return } // 2. Create Queues _, err = rbt.channel.QueueDeclare( rbt.QueueName, // Here we pass in empty, then the name of the randomly generated queue true, false, false, false, nil, ) if err != nil { log.Printf("rbt.channel.QueueDeclare error : %v", err) return } // 3. Binding Queue err = rbt.channel.QueueBind( rbt.QueueName, // Queue name rbt.Key, // routing, key needs to be filled in here rbt.Exchange, // Switch name false, // Is Blocking nil, // Other Properties ) if err != nil { log.Printf("rbt.channel.QueueBind error : %v", err) return } } // Production message publish func (rbt *RabbitMQ) ProduceRouting(data []byte) { // 1. Add data to the queue err := rbt.channel.Publish( rbt.Exchange, // Switch rbt.Key, // key false, // If true, failing to find a qualified queue based on its exchange type and routekey rule will return the message to the sender false, // If true, when exchange sends a message to the queue and finds that there are no consumers in the queue, it returns the message to the sender amqp.Publishing{ ContentType: "text/plain", Body: data, }, ) if err != nil { log.Printf("rbt.channel.Publish error : %v", err) return } return } // Consumer News func (rbt *RabbitMQ) ConsumeRoutingMsg() { // 4. Consumption data msg, err := rbt.channel.Consume( rbt.QueueName, // Queue name "", // Name of the consumer true, // Whether to auto-answer false, // Is it exclusive false, // true means that messages from producers in the same Conenction cannot be delivered to consumers in this Connection false, // Is Blocking nil, // Other Properties ) 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 Model Case Scenarios: Get the corresponding function string from the system's code logic, throw message tasks into the corresponding queue for business scenarios, such as handling errors, handling specific messages, and so on Production message */ func main() { log.SetFlags(log.Llongfile | log.Ltime | log.Ldate) rbt1 := &xmtmq.RabbitMQ{ Exchange: "xmtPubEx2", Key: "xmt1", QueueName: "Routingqueuexmt1", MQUrl: "amqp://guest:[email protected]:5672/xmtmq", } xmtmq.NewRabbitMQ(rbt1) rbt1.Init() rbt2 := &xmtmq.RabbitMQ{ Exchange: "xmtPubEx2", Key: "xmt2", QueueName: "Routingqueuexmt2", MQUrl: "amqp://guest:[email protected]:5672/xmtmq", } xmtmq.NewRabbitMQ(rbt2) rbt2.Init() var index = 0 for { rbt1.ProduceRouting([]byte(fmt.Sprintf("hello wolrd xmt1 %d ", index))) log.Println("Send Successfully xmt1 ", index) rbt2.ProduceRouting([]byte(fmt.Sprintf("hello wolrd xmt2 %d ", index))) log.Println("Send Successfully 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:[email protected]: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:[email protected]:5672/xmtmq", } xmtmq.NewRabbitMQ(rbt) rbt.ConsumeRoutingMsg() xmtmq.RabbitMQFree(rbt) }

topic mode

Topic mode, where a message is retrieved by multiple consumers and the target queue of the message is available with the BindingKey wildcard

Topics mode is actually a routing mode

The biggest difference between them is that Topics mode sends messages and consumer messages by matching them with wildcards

  • A * number means you can use a word with it
  • A #number means that you can use zero or more words together

The coded case remains the same as the routing pattern above, except the exchange is of top type

The following are the switches and queues involved in the above modes

rpc mode

RPC remote procedure calls, client calls server-side methods remotely, and uses MQ to make RPC asynchronous calls

The directory structure is:

. ├── consumer.go ├── go.mod ├── go.sum ├── main.go └── xmtmq └── xmtmq.go
  • Clients are both producers and consumers, sending RPC call messages to RPC request queues and listening for RPC response queues
  • The server listens for messages from the RPC request queue, executes the server-side method after receiving the message, and gets the result returned by the method
  • The server sends the results of the RPC method to the RPC response queue.
  • Client listens on RPC response queue and receives the result of RPC call

xmtmq.go

package xmtmq import ( "github.com/streadway/amqp" "log" "math/rand" ) // rpc mode // Define the data structure of RabbitMQ // go get github.com/streadway/amqp type RabbitMQ struct { conn *amqp.Connection // Connect channel *amqp.Channel // passageway QueueName string // Queue name Exchange string // Switch Key string // Routing Key MQUrl string // Virtual Machine Address for MQ } // New a 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() } // Production message func (rbt *RabbitMQ) Produce(data []byte) { // Application queue q, err := rbt.channel.QueueDeclare( rbt.QueueName, // Queue name true, // Is it persistent false, // Whether to delete automatically false, // Is it exclusive false, // Is Blocking nil, // Other parameters ) 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) } // Consumer News func (rbt *RabbitMQ) Consume() { // Application queue q, err := rbt.channel.QueueDeclare( "", // Queue name true, // Is it persistent false, // Whether to delete automatically false, // Is it exclusive false, // Is Blocking nil, // Other parameters ) if err != nil { log.Printf("rbt.channel.QueueDeclare error : %v", err) return } // Consumption data msg, err := rbt.channel.Consume( q.Name, // Queue name "xmt", // Name of the consumer true, // Whether to auto-answer false, // Is it exclusive false, // true means that messages from producers in the same Conenction cannot be delivered to consumers in this Connection false, // Is Blocking nil, // Other Properties ) 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 Model Case Scenario: Use of simple message queues, one producer and one consumer Production message */ func main() { log.SetFlags(log.Llongfile | log.Ltime | log.Ldate) rbt := &xmtmq.RabbitMQ{ QueueName: "xmtqueue", MQUrl: "amqp://guest:[email protected]: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:[email protected]:5672/xmtmq", } xmtmq.NewRabbitMQ(rbt) rbt.Consume() }

Let's run the consumer first, run a few more, and you can see that we already have data in our queue. We run two consumers, so here's 2

By running the producer again, you can see that the producer consumes the message sent by the consumer, and through the correlation Id, you can find the queue corresponding to the consumer's monitoring and send the data to the queue

When there is data in the queue that consumers monitor, they take it out for consumption

summary

Six working modes of RabbitMQ:

  • single mode
  • work mode
  • publish / subscribe mode
  • routing mode
  • topic mode
  • rpc mode

Reference material:

RabbitMQ Tutorials

Welcome to Comment, Attention, Collection

Friends, your support and encouragement are my motivation to keep sharing and improve quality

Okay, that's it this time

Technology is open, our mindset should be more open. Embrace change, live in the sun and work hard to move forward.

I'm a little devil. Welcome to your collection. See you next time.

30 September 2021, 13:14 | Views: 1420

Add new comment

For adding a comment, please log in
or create account

0 comments