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:
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.