Using RabbitMQ in Java

Keywords: Java RabbitMQ network

rely on

        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.8.0</version>
        </dependency>

 

 

 

 

 production public class Producer {    private final static String QUEUE_NAME = "my_queue"; //Queue name

private final static String EXCHANGE_NAME = "my_exchange"; //To be used exchange Name
    private final static String EXCHANGE_TYPE = "topic"; //To be used exchange Name
    private final static String EXCHANGE_ROUTING_KEY = "my_routing_key.#"; //exchange Used routing-key

    public static void send() throws IOException, TimeoutException {
        //Create connection factory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.1.9"); //Set up rabbitmq-server Address
        connectionFactory.setPort(5672);  //Port number used
        connectionFactory.setVirtualHost("/");  //Virtual host used

        //Create connection from connection factory
        Connection connection = connectionFactory.newConnection();

        //Creating channels through connections
        Channel channel = connection.createChannel();

        //Declare a exchange,If it already exists, it will be used directly. If it does not exist, it will be created automatically
    
channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE, true, false, false, null); //Declare a queue,If this queue already exists, it will be used directly; if not, it will be created automatically
    
channel.queueDeclare(QUEUE_NAME, true, false, false, null); //take queue Bind to a exchange. One exchange Can bind multiple queue channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTING_KEY); //send message String msg = "hello"; //Message content String routing_key = "my_routing_key.key1"; //send messageUsed routing-key channel.basicPublish(EXCHANGE_NAME,routing_key,null,msg.getBytes()); System.out.println("send message: "+msg); //Close connection channel.close(); connection.close(); } }

 

 

 

 

exchange details

rabbitmq comes with seven switches, all starting with amq. We can use our own switches or create new ones ourselves.

 

 

Switch parameters

First look at the bottom add a new exchange:

  • Name name of switch
  • Type: the type of switch. To put it bluntly, which matching rule is used for routing key matching queue
  • Whether durability messages support persistence? Durable is to support persistence. Restart rabbitmq. Messages on rabbitmq are still and will not be lost. D in features above is durable; transient is not to support persistence. Restart rabbitmq. Messages on rabbitmq are lost. Because the message is saved in the internal junior high school, it will not be persisted to the hard disk, and the rabbitmq message will be lost directly when it is closed, and the message will be loaded from the hard disk when it is restarted after persistence. The transient keyword is used to block serialization.
  • auto delete if a queue of this switch is not bound, do you want to delete this switch automatically
  • Internal whether this switch is only used inside rabbitmq. Most switches need to be exposed for message producers. Only a few (usually brought by rabbitmq) are used internally. I in features is internal, which means it is only used internally. amq.rabbitmq.trace is used to track the message delivery process inside rabbitmq, which is only used internally.
  • arguments set some other parameters for this switch

 

//Declare a exchange,If it already exists, it will be used directly. If it does not exist, it will be created automatically
channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE, true, false, false, null);

Declare a switch. The parameters are in the same order as those of add a new exchange on the above console. arguments are represented by a map. Generally, you do not need to set other parameters and write them as null.

 

The first of the seven switches is (AMQP default), which does not mean that the name of the switch is AMQP default.

The name of this switch has no name (empty). If you want to use this switch, the name of the switch in the code should be written as an empty string. Parentheses indicate that this switch is rabbitmq's default switch.

 

 

 

 

Four types of switches

When Binding exchange and queue, we use a routing key:

 

private final static String EXCHANGE_ROUTING_KEY = "my_routing_key.#"; //exchange Used routing-key
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTING_KEY);

The exchange uses routing key to match the bound queue, that is, the queues to which messages are sent.

 

 

Another routing key is used when sending messages:

 

 String routing_key = "my_routing_key.key1"; //Used for sending messages routing-key

 channel.basicPublish(EXCHANGE_NAME,routing_key,null,msg.getBytes());

rabbitmq will send this message to the specified exchange,



How to match? Is it a perfect match or a fuzzy match? This involves four type s of exchange:

 

(1) direct exactly matches

The routing key used to send messages should be exactly the same as the routing key used by the switch.

For example, the routing key used by exchange is "my routing key", and the routing key used to send messages is also "my routing key"

 

 

(2) topic fuzzy matching, wildcards can be used

*Only one word can be matched, Chen can match one or more words.

Notice that it's a word, not a character. "my_routing_key.key1", my_routing_key is a word, key1 is a word, and the words are separated by dots.

For example, if the routing key of exchange is "my routing key." then the routing key used to send messages starts with "my routing key."

This is what I used in the example. This method is very suitable for delivering a message to multiple queue s

 

 

(3) fanout broadcast mode

The routing key is not used. Generally, the routing key is set to an empty string. Of course, it is OK to set any string, but it is not used anyway.

Exchange will post (broadcast) messages to all queue s bound to this exchange.

This pattern is very efficient, because it does not match routing key, which greatly reduces the time cost.

 

 

(4) headers head mode (just understand)

Instead of using routing key, post the message to the matching queue according to the header.

Map<String, Object> exchange_headers = new Hashtable<String, Object>();
headers.put("x-match", "any"); //Specify key value pair matching pattern any,all
headers.put("key1", "value1");  //Put in some key value pairs
headers.put("key2", "value2");

//binding queue Specify when map. As for routing-key,Set whatever string you want, but don't use it anyway
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME,"", exchange_headers);


//Also use when sending messages map
Map<String,Object> headers = new Hashtable<String, Object>();
headers.put("key1", "value1");  //Put some key value pairs


Builder properties = new BasicProperties.Builder();
properties.headers(headers);


String msg="hello";
//Specify the message to use header. To use properties Can't send directly map. Will be placed in http Request header
channel.basicPublish(EXCHANGE_NAME, "",properties.build(),msg.getBytes());

x-match specifies the matching pattern. All: the key value pairs in the header (map) of the sent message should be the same as all the key value pairs in the header of the exchange. The header of the exchange has two key value pairs, key1 and key2. The header of the sent message should also have these two key value pairs (they need to be exactly the same). any: as long as there is a key value pair in the header of sending message that is exactly the same as the key value pair in exchange, for example, key1 and key2 are OK, as long as there is one.

header is not commonly used because it is a bit complicated. It's not easy to use routing key. Does it smell bad? It has to be so complicated.

 

However, we still need to understand the use of properties:

A message consists of properties and body, which are the last two parameters of basicPublish().

Properties can set some parameters of this message, such as delay delivery and priority. These parameters are written as key value pairs and put in the map. The map is converted to properties, and then properties is used as the parameter of basicPublish().

 

 

 

 

Queue detailed explanation

 

Type: Specifies the queue type. It defaults to classic (main queue) and can also be set to quorum (slave queue). It synchronizes the main queue to the slave queue. When the main queue fails, the slave queue can also be used.

Name: the name of this queue

durability: whether messages in the queue are persisted to the hard disk. Exchange also has this property. The message will be sent to exchange for saving, and then exchange will be delivered to some queues. When the consumer hasn't processed the message, the message will be saved in the queue.

auto delete: automatically delete the queue if it is not bound to any exchange

arguments: set some other parameters

 

//Declare a queue
//Parameters: name,Whether it supports persistence, exclusive, automatic deletion and other parameters( map)
channel.queueDeclare(QUEUE_NAME, true, false, false, null);

Exclusive: this queue can only be used in the current connection (rejected in other connections). It is created by the current connection declaration |, and will be deleted automatically when the connection is disconnected.

 

An exchange can be bound to multiple gueue s, and a queue can also be bound to multiple exchanges.





 

Consumer

public class Consumer {
    private final static String QUEUE_NAME = "my_queue"; //Queue name

    public static void receive() throws IOException, TimeoutException {
        //Create connection factory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.1.9"); //Set up rabbitmq-server Address
        connectionFactory.setPort(5672);  //Port number used
        connectionFactory.setVirtualHost("/");  //Virtual host used

        //Create connection from connection factory
        Connection connection = connectionFactory.newConnection();

        //Creating channels through connections
        Channel channel = connection.createChannel();

        //Create a consumer and specify the channel. QueueingConsume Class is deprecated, use DefaultConsumer replace
        DefaultConsumer consumer = new DefaultConsumer(channel){
            //Monitoring queue When a message comes in, this method is automatically called to process the message. But this method is empty by default and needs to be overridden
            @Override
            public void handleDelivery(java.lang.String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                java.lang.String msg = new java.lang.String(body);
                System.out.println("received msg: " + msg);
            }
        };

        //Listen to the specified queue. It will be monitored all the time.
        //Parameter: to monitor queue,Whether to automatically confirm the message and used Consumer
        channel.basicConsume(QUEUE_NAME, true, consumer);

    }
}

On the surface, there is no problem with this code, so listening to the queue is over. But there is a hidden danger:

We declare the created exchange and queue in the producer. If the producer has not yet run and there is no corresponding exchange and queue (not created before) on rabbitmq, start the consumer. If the consumer wants to listen to the specified queue, they cannot connect to the queue at all. The specified queue creation has not been created. What do they listen to? Direct error reporting.

 

 

A more robust way to write it is to also declare exchange and queue in the consumer. If you have them, they can be used directly, but not created automatically.

public class Consumer {
    private final static String QUEUE_NAME = "my_queue"; //Queue name
    private final static String EXCHANGE_NAME = "my_exchange"; //To be used exchange Name
    private final static String EXCHANGE_TYPE = "topic"; //To be used exchange Name
    private final static String EXCHANGE_ROUTING_KEY = "my_routing_key.#"; //exchange Used routing-key

    public static void receive() throws IOException, TimeoutException {
        //Create connection factory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.1.9"); //Set up rabbitmq-server Address
        connectionFactory.setPort(5672);  //Port number used
        connectionFactory.setVirtualHost("/");  //Virtual host used

        //Create connection from connection factory
        Connection connection = connectionFactory.newConnection();

        //Creating channels through connections
        Channel channel = connection.createChannel();

        //Declare a exchange,If it already exists, it will be used directly. If it does not exist, it will be created automatically
        channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE, true, false, false, null);

        //Declare a queue,If this queue already exists, it will be used directly; if not, it will be created automatically
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        //take queue Bind to a exchange. One exchange Can bind multiple queue
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTING_KEY);

        //Create a consumer and specify the channel. QueueingConsume Class is deprecated, use DefaultConsumer replace
        DefaultConsumer consumer = new DefaultConsumer(channel){
            //Monitoring queue When a message comes in, this method is automatically called to process the message. But this method is empty by default and needs to be overridden
            @Override
            public void handleDelivery(java.lang.String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                java.lang.String msg = new java.lang.String(body);
                System.out.println("received msg: " + msg);
            }
        };

        //Listen to the specified queue. It will be monitored all the time.
        //Parameter: to monitor queue,Whether to automatically confirm the message and used Consumer
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
    
}

 

 

queue of rabbitmq console:

ready is the number of messages to be delivered in this queue, unacked is the number of messages delivered but not confirmed by consumers (similar to the number of signed in and not received by express delivery). Total is the total number of messages, that is, the sum of the first two.

incoming is the time it takes for a message to enter the queue from exchange

Delivery / get is the time it takes for a message to be delivered from the queue to the consumer,

ack is how long does it take for the queue to receive a response from the consumer after a message is delivered to the consumer.

/s is in seconds

 

 

ack confirmation and response.

The consumer receives the message delivered by the queue, then processes the message, and sends a data packet to the queue as a confirmation and response (equivalent to getting the package, trying it out, no problem, receiving the goods),

After the queue delivers the message to the consumer, the message remains in the queue and will not be deleted until the consumer answers.

 

 

//Parameter: to monitor queue,Whether to automatically confirm the message and used Consumer
channel.basicConsume(QUEUE_NAME, true, consumer);

The second parameter: autoAck, auto answer or not.

true: automatic response. When the queue delivers the message to the consumer, it is considered that the consumer has signed in. When a message is delivered, the message will be deleted directly.

This is not reliable. If the consumer fails before they have finished processing, the message will be lost and not processed.

fasle: automatic response is not used, and the consumer needs to answer by himself.

 

 

Consumer manual response:

     //Create a consumer and specify the channel. QueueingConsume Class is deprecated, use DefaultConsumer replace
        DefaultConsumer consumer = new DefaultConsumer(channel){
            //Monitoring queue When a message comes in, this method is automatically called to process the message. But this method is empty by default and needs to be overridden
            @Override
            public void handleDelivery(java.lang.String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                java.lang.String msg = new java.lang.String(body);
                System.out.println("received msg: " + msg);
                channel.basicAck(envelope.getDeliveryTag(), false);  //After processing, reply and sign in
                //channel.basicReject(envelope.getDeliveryTag(), true); / / reject
            }
        };

        //Listen to the specified queue. It will be monitored all the time.
        //Parameter: to monitor queue,Whether to automatically confirm the message and used Consumer
        channel.basicConsume(QUEUE_NAME, false, consumer);

Even if the consumer goes down while processing the message, as long as there is no response, the message in the queue will always exist, and the consumer will post the message when it starts again.

The first parameter of basicAck() is DeliveryTag, which uniquely identifies a message in a queue, equivalent to the id of a message.

The second parameter is multiple, multiple, batch processing. Whether to put the replies of multiple messages together and send them to the queue at one time can reduce the network traffic and prevent the network from blocking if it is set to true, but the replies of previous messages have delay.

 

 

If there is an exception (code execution problem) when processing the message, reject in catch:

catch (...){ 
  channel.basicReject(envelope.getDeliveryTag(), true); //Reject, rejoin
  //... / / log
}

The second parameter is request. If you want to re-enter the queue, set it to fasle. If you don't re re-enter the queue again, the queue will delete this message. If you set it to true, the queue will re-enter the queue, and the queue will re deliver this message to the consumer.

There is no need to put the whole processing flow of the message in try, just put the code blocks that may have exceptions in try, and reject them in catch.

 

This is the reliable delivery mechanism provided by rabbitmq. Combined with the persistence of messages, rabbitmq achieves high reliability.

 

But there's a problem with rejoining: if a large number of messages are rejoined, the re delivery of these messages will take up resources and slow down the delivery of other messages.

 

 

 

 

Possible problems in the development process

1. The name of exchange uniquely identifies an exchange. During debugging, the type of exchange may be modified. If there is an exchange with the same name before, an error will be reported.

If the previous exchange with the same name is not available, just delete the exchange with the same name from the rabbitmq console; if the previous exchange with the same name needs to be used, change the current exchange to the name.

 

2. The consumer has to listen to the queue all the time, so the channel and connection of the consumer are not closed. When the user starts again, the connection may not be connected, and an error will be reported, because the connection is still maintained on rabbitmq.

Wait a few minutes to run, and wait for the connection timeout to be deleted.

 

 

 

Explain

1. You can set the binding of the queue and send messages to the queue in the rabbitmq console

delivery

persistent. persistent. persistent. Indicates that this message will be persisted to the hard disk.

payload is the body of the message.

 

 

2. Validity of message

Some messages have a high demand for immediacy. After some time, if the message is still backlog in the queue, the message may have no use value. There is no need to post it again. You need to delete the message.

You can set the validity period of the message. If the message is not delivered within the specified time, the queue will automatically delete the message and no longer deliver it.

Specific code reference: https://blog.csdn.net/liu0808/article/details/81356552

Posted by walexman on Tue, 17 Mar 2020 07:11:04 -0700