RabbitMQ, rabbitmqandroid
This time, we will try the publish/subscribe mode,
That is, a message is sent to multiple consumers.
A simple applet is used to describe publish/subscribe.
A message is provided by a provider, which is received by multiple consumers.
Consumer makes different responses to the same message, such as printing, saving to a file, database, or something.
The previous example may give people this feeling:
The producer sends the message to the queue. The message is buffered in the queue, and the consumer obtains the message from the queue.
But this is not true.
In rabbit, the producer never directly sends messages to the queue.
The producer does not know whether the message will be sent to a queue.
In fact, producer can only send messages to exchange.
Although it seems that there is one more thing, exchange is not complicated.
Exchange only obtains messages from the producer and pushes messages to the queue.
But why is there another step?
For example, after receiving a message, exchange should push the message to a specific queue? Or can I push messages to multiple queues? Or directly discard the message?
These rules depend on the type of exchange.
The following are some available exchange types (org. springframework. amqp. core. ExchangeTypes ):
public static final String DIRECT = "direct";public static final String TOPIC = "topic";public static final String FANOUT = "fanout";public static final String HEADERS = "headers";public static final String SYSTEM = "system";
We can define an exchange as follows:
channel.exchangeDeclare("logs", "fanout");
Like fanout, fanout sends messages to all accessible queues.
How to view the defined exchange?
View the defined exchange, we can use the rabbitmqctl list_exchanges command,
In the figure, both amq. * and exchange without a name are provided by default.
(PS: we have not used the concept of exchange in the previous example, but still successfully sent the message to the queue.
This is because we use the default exchange .)
We need to send the message to the specified exchange.
The first parameter of basicPublish is the name of exchange ).
An empty string indicates the default exchange:
channel.basicPublish( "logs", "", null, message.getBytes());
Queue naming is very important. For example, multiple workers share one queue, and the relationship between producer and consumer is maintained by the queue name.
However, not all scenarios need to be named in person.
For example, we need to get all the messages, not a subset of them.
Or we are more concerned about the latest messages than those put in the queue earlier.
We need to have the server name queue randomly, and the queue is automatically deleted when the consumer connection is disconnected.
We only need a line of code to do this:
String queueName = channel.queueDeclare().getQueue();
Call queueDeclare () without parameters to create a temporary queue.
Now we have created exchange and queue.
We need to associate them with something called "binding ".
Use the following code to associate them:
channel.queueBind(queueName, "logs", "");
As with viewing exchange, we can use the rabbitmqctl list_bindings command to view binding.
The diagram from producer to queue is as follows:
I wrote a Channel static factory, which is not well written.
I plan to define two exchanges in the static initialization block:
Final class ChannelFactory {// temporary queue of the consumer and the two exchanges are bound to final static String EXCHANGE_NAME = "log"; final static String EXCHANGE_NAME _ = "log2 "; private static final ConnectionFactory factory = new ConnectionFactory (); static {try {Channel temp = getChannel (); temp. exchangeDeclare (EXCHANGE_NAME, ExchangeTypes. FANOUT); temp. exchangeDeclare (EXCHANGE_NAME _, ExchangeTypes. FANOUT); closeChannel (temp);} catch (IOException e) {e. printStackTrace () ;}} private ChannelFactory () {} public static Channel getChannel () {try {return factory. newConnection (). createChannel ();} catch (IOException e) {e. printStackTrace ();} return null;} public static Channel getChannel (int channelNumber) {try {return factory. newConnection (). createChannel ();} catch (IOException e) {e. printStackTrace ();} return null;} public static void closeChannel (Channel channel) {try {channel. close (); channel. getConnection (). close ();} catch (IOException e) {e. printStackTrace ();}}}
Producer class, the same producer sends messages to two exchange:
class Publisher { public static void main(String[] args) throws IOException { Channel channel = ChannelFactory.getChannel(); String message = "Here is the content"; channel.basicPublish(ChannelFactory.EXCHANGE_NAME, StringUtils.EMPTY, null, ("EXCHANGE_NAME 1:::"+message).getBytes()); channel.basicPublish(ChannelFactory.EXCHANGE_NAME_, StringUtils.EMPTY, null, ("EXCHANGE_NAME 2:::"+message).getBytes()); ChannelFactory.closeChannel(channel); }}
Consumer class, temporary queue needs to be bound with two exchange:
class Logger { public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException { Channel channel = ChannelFactory.getChannel(); String queue = channel.queueDeclare().getQueue(); System.out.println("temporary queue name::"+queue); channel.queueBind(queue, ChannelFactory.EXCHANGE_NAME, ""); channel.queueBind(queue, ChannelFactory.EXCHANGE_NAME_, ""); QueueingConsumer consumer = new QueueingConsumer(channel); channel.basicConsume(queue, true, consumer); while (true) { System.out.println(new String(consumer.nextDelivery().getBody())); } }}
Because temporary queues are used, you need to run consumer before running producer.
Running result output: