In the previous article, we created the work queue. Behind the work queue, rabbitmq only sends each task message to one consumer. This article describes how to push a message to multiple consumers. This mode is called publish/subscribe (publish/subscribe ).
To illustrate this mode, we will build a simple log system. This will contain two programs, the first is to send log information, and the second will receive and print them.
In our log system, every running consumer program can receive messages. In this way, I run a consumer and write the logs to the disk. At the same time, we run another consumer to print the log messages to the screen.
In essence, log messages are pushed to all consumers.
1. Message Switch
In the previous article, we will send messages to the queue and retrieve the messages from the queue. Now we will introduce the full message model of rabbitmq.
Let's review the content in the previous blog:
- A producer application sends messages;
- A message queue is used to store and cache messages;
- A consumer program receives messages
The core idea of rabbitmq's message sending model is that the producer does not directly send messages to the message queue. In fact, the producer does not know the queue to which the message will be cached.
In fact, the producer can send messages to exchange (message switch. Exchange is a simple task. It receives messages from producers and pushes messages to message queues. Exchange must know how to process a message it receives. Should I push this message to the specified message queue? Or push messages to all queues? Or discard the message? These rules can be defined using the exchange type.
There are some available exchange types: direct, topic, headers and fanout. Here we mainly look at the last one: fanout. Here we create an exchange named logs and of the fanout type:
Channel. exchangedeclare ("logs", "fanout ");
Fanout type exchange is very simple. It broadcasts all the messages it receives to all the queues it knows. This is exactly what our log system needs.
List exchange:
You can use the rabbitmqctl command on the server to list all messages exchange on the rabbitmq Server:
$ sudo rabbitmqctl list_exchangesListing exchanges ... directamq.direct directamq.fanout fanoutamq.headers headersamq.match headersamq.rabbitmq.log topicamq.rabbitmq.trace topicamq.topic topiclogs fanout...done.
In this list, there are some exchanges in the form of AMP. * and default (untitled) switches. These are all created by default, but none of these created by default are what you need now.
Exchange with no name:
In the previous blog, I didn't know how to use exchange, but I was able to send messages. The message can be sent successfully because we use a default exchange, which is identified.
channel.basicPublish("", "hello", null, message.getBytes());
The first parameter is the name of exchange. The Null String refers to the default or unnamed exchange: messages are routed to the specified Message Queue according to the routingkey.
Now let's push the message to the named exchange:
channel.basicPublish( "logs", "", null, message.getBytes());
2. Temporary queue
If you have read the previous blog posts, you will find that we all use a specified name Message Queue (hello and task_queue ). The corresponding producer and consumer must use the same message queue name, which is very important.
However, this is not the case in our log system. We hope to receive all log messages, not just a part of them. We only need to process the current log message without having to worry about historical logs. To achieve this, we need to do the following two steps:
- Whenever we establish a connection with rabbitmq, We need to refresh and clear the queue. To achieve this goal, we can use a random name (which can be defined by ourselves) to create a queue, or let the server automatically create an on-demand queue.
- When the consumer is disconnected, the queue can be automatically deleted.
When using the Java client, we can use the queuedeclare method without parameters to create an exclusive and automatically deleted queue with the generated name:
String queueName = channel.queueDeclare().getQueue();
This is the queue with a random name, like AMQ. gen-JzTY20BRgKO-HjmUJj0wLg.
3. bindings)
We have created a fanout type exchange and a queue. Now we need exchange to send messages to our queue. The relationship between exchange and queue is called binding ).
channel.queueBind(queueName, "logs", "");
Now, the exchange named logs will forget to return the message in our queue.
View bindingList:
Use the rabbitmqctl list_bindings command to view all existing bindings.
4. Final Implementation
There is not much difference between the producer program that sends log messages and the previous program. The biggest difference is that we push messages to a named exchange instead of the default exchange that was not previously named. We need to provide a routingkey when sending a message, but we can ignore fanout exchange. Below is the producer code emitlog. Java:
Import Java. io. ioexception; import COM. rabbitmq. client. connectionfactory; import COM. rabbitmq. client. connection; import COM. rabbitmq. client. channel; public class emitlog {Private Static final string exchange_name = "logs"; public static void main (string [] argv) throws Java. io. ioexception {connectionfactory factory = new connectionfactory (); factory. sethost ("localhost"); connection = factory. newconnection (); channel = connection. createchannel (); // declare the exchange name and type channel. exchangedeclare (exchange_name, "fanout"); // for the implementation of getmessage, see the previous blog post string message = getmessage (argv); // specify the exchange name channel. basicpublish (exchange_name, "", null, message. getbytes (); system. out. println ("[x] sent '" + message + "'"); channel. close (); connection. close ();}//...}
As you can see, after establishing a connection, we declare exchange. This step is required because messages cannot be pushed to a non-existing exchange.
If the queue is not responsible for exchange, the message will be lost, which is no problem; if there is no consumer listening, we will safely lose these messages.
The receivelogs. Java code is as follows:
Import Java. io. ioexception; import COM. rabbitmq. client. connectionfactory; import COM. rabbitmq. client. connection; import COM. rabbitmq. client. channel; import COM. rabbitmq. client. queueingconsumer; public class extends elogs {Private Static final string exchange_name = "logs"; public static void main (string [] argv) throws Java. io. ioexception, Java. lang. interruptedexception {connectionfactory factory = new connectionfactory (); factory. sethost ("localhost"); connection = factory. newconnection (); channel = connection. createchannel (); // declare the name and type of the message route. exchangedeclare (exchange_name, "fanout"); // declare a random Message Queue string queuename = channel. queuedeclare (). getqueue (); // bind the message queue and message routing channel. queuebind (queuename, exchange_name, ""); system. out. println ("[*] waiting for messages. to exit press Ctrl + C "); queueingconsumer consumer = new queueingconsumer (Channel); // start a consumer channel. basicconsume (queuename, true, consumer); While (true) {queueingconsumer. delivery delivery = consumer. nextdelivery (); string message = new string (delivery. getbody (); system. out. println ("[x] received'" + message + "'");}}}
Javac-CP.: amqp-client-3.3.5.jar receivelogs. Java emitlog. Java
Java-CP.: amqp-client-3.3.5.jar receivelogs> logs_from_rabbit.log
Then listen to the log file:
Tail-10f logs_from_rabbit.log
- Print log messages to the screen:
Java-CP.: amqp-client-3.3.5.jar receivelogs
Java-CP.: amqp-client-3.3.5.jar emitlog
Log output to file:
Log messages are printed to the screen:
When running receivelogsUse rabbitmqctl list_bindingsCommand to view rabbitmqExchange:
[email protected]:~$ sudo rabbitmqctl list_bindingsListing bindings ... exchange amq.gen-1Zuyn_44c8IWsdJWrI42Og queue amq.gen-1Zuyn_44c8IWsdJWrI42Og [] exchange amq.gen-rSrGSPWLNTuq1dfXipPfAA queue amq.gen-rSrGSPWLNTuq1dfXipPfAA [] exchange task_queue queue task_queue []logs exchange amq.gen-1Zuyn_44c8IWsdJWrI42Og queue []logs exchange amq.gen-rSrGSPWLNTuq1dfXipPfAA queue []...done.
Summary:
1. Declare the exchange name and type on the producer and consumer channels.
2. Specify the exchange destination in the producer channel
3. Declare a random message queue in the consumer channel and obtain the queue name. Then, bind the message queue and message route on the channel.
In the next article, we will discuss how the consumer can obtain part of the messages sent by the producer.
Reference: http://www.rabbitmq.com/tutorials/tutorial-three-java.html
Article 4 of the rabbitmq series: publish/subscribe publish/Subscribe