RabbitMQ message mode II

Keywords: RabbitMQ Java

Blog content

Portal

Getting started with RabbitMQ
RabbitMQ message mode 1

Current restriction at the consumer end


What is the current restriction of the consumer end?

Suppose a scenario, first of all, our RabbitMQ server has tens of thousands of unprocessed messages, and we open a consumer client casually, and the following situation will appear:
Huge amount of messages are pushed all at once, but our single client can't handle so much data at the same time!

The existing business scenario of current restriction at the consumer end

For example, in our case, after the user pays, a message will be sent to MQ Broker. If the 100 elder brother user pays, then there will be 100 messages in MQ Broker (message sending)
The above situation will bring about a problem. When the SMS service has been sent down due to the problem of SMS interface, the 100 messages need to be processed in an instant as soon as the SMS micro service is started. As a result, the SMS micro service will be suspended again

Solutions provided by RabbitMQ for current limiting at the consumer end

RabbitMQ provides a QoS (quality of service) function, that is, on the premise of not automatically confirming messages, if a certain number of messages (by setting the value of QoS based on the Consumer or Channel) are not confirmed, new messages will not be consumed
Void BasicQos(uint prefetchSize, ushort prefetchCount, bool global);
prefetchSize: 0 does not limit message size
 Here's the prefetchSize
 prefetchSize: will tell RabbitMQ not to push more than N messages to a Consumer at the same time, that is, once there are N messages without ack, the Consumer will block until there is ack
 Global: true\false whether to apply the above settings to the Channel; in short, whether the above restrictions are Channel level or Consumer level

Custom consumer

package com.onlyk.api.rabbitmqapi.limit;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 9:46
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class MyConsumer extends DefaultConsumer {
    private Channel channel ;

    public MyConsumer(Channel channel) {
        super(channel);
        this.channel = channel;
    }

    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.err.println("-----------consume message----------");
        System.err.println("consumerTag: " + consumerTag);
        System.err.println("envelope: " + envelope);
        System.err.println("properties: " + properties);
        System.err.println("body: " + new String(body));

        channel.basicAck(envelope.getDeliveryTag(), false);
    }

}

Consumer code

package com.onlyk.api.rabbitmqapi.limit;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 9:47
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class Consumer {
    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("140.143.242.67");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();


        String exchangeName = "test_qos_exchange";
        String queueName = "test_qos_queue";
        String routingKey = "qos.#";

        channel.exchangeDeclare(exchangeName, "topic", true, false, null);
        channel.queueDeclare(queueName, true, false, false, null);
        channel.queueBind(queueName, exchangeName, routingKey);

        //1. The first thing about current limiting mode is that autoAck is set to false
        channel.basicQos(0, 1, false);

        channel.basicConsume(queueName, false, new MyConsumer(channel));
    }
}

Production end code

package com.onlyk.api.rabbitmqapi.limit;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 9:48
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class Producer {
    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("140.143.242.67");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        String exchange = "test_qos_exchange";
        String routingKey = "qos.save";

        String msg = "Hello RabbitMQ QOS Message";

        for(int i =0; i<5; i ++){
            channel.basicPublish(exchange, routingKey, true, null, msg.getBytes());
        }

    }
}

Start the consumer end

Start production side

ACK and return queue of messages


Consumer manual ACK and NACK
When the consumer is consuming, if the business is abnormal, we can record the logs and then make compensation
If there are serious problems such as server downtime, we need to manually perform ACK to ensure the success of consumer consumption!
Consumer's return queue
The purpose of the consumer returning to the queue is to return the message to the Broker for messages that have not been processed successfully!
Generally, we will close the return queue in the actual application, that is to say, it is set to False; because there is a high probability that the return queue message will still fail to be processed!

Custom consumer

package com.onlyk.api.rabbitmqapi.ack;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 10:04
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class MyConsumer extends DefaultConsumer {
    private Channel channel;
    
    public MyConsumer(Channel channel) {
        super(channel);
        this.channel = channel;
    }
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.err.println("-----------consume message----------");
        System.err.println("body: " + new String(body));
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if ((Integer) properties.getHeaders().get("num") == 0) {
            System.out.println("Simulate the failure of the third-party amount opening and calling method of SMS microservice...");
//            Manually sign in and return to the queue (the third-party interface calls exception handling) and write it in catch
            channel.basicNack(envelope.getDeliveryTag(), false, true);
        } else {
            channel.basicAck(envelope.getDeliveryTag(), false);
        }
    }
}

Consumer code

package com.onlyk.api.rabbitmqapi.ack;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 10:06
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class Consumer {
    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("140.143.242.67");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();


        String exchangeName = "test_ack_exchange";
        String queueName = "test_ack_queue";
        String routingKey = "ack.#";

        channel.exchangeDeclare(exchangeName, "topic", true, false, null);
        channel.queueDeclare(queueName, true, false, false, null);
        channel.queueBind(queueName, exchangeName, routingKey);

        // Manual sign in must close autoAck = false
        channel.basicConsume(queueName, false, new MyConsumer(channel));
    }
}

Production end code

package com.onlyk.api.rabbitmqapi.ack;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 10:06
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class Produces {
    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("140.143.242.67");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        String exchange = "test_ack_exchange";
        String routingKey = "ack.save";

        for(int i =0; i<5; i ++){
            Map<String, Object> headers = new HashMap<String, Object>();
            headers.put("num", i);
            AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                    .deliveryMode(2)
                    .contentEncoding("UTF-8")
                    .headers(headers)
                    .build();
            String msg = "Hello RabbitMQ ACK Message " + i;
            channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
        }
    }
}

Start the consumer end

Start the production side (0 will always resend)

TTL message

TTL is the abbreviation of Time To Live, that is, Time To Live
Effect

RabbitMQ supports message expiration time, which can be specified when sending messages
 RabbitMQ supports the expiration time of the queue. It starts from the time when the message enters the queue. As long as the timeout configuration of the queue is exceeded, the message is automatically cleared

Note: it is mainly for message setting, which has nothing to do with switch, queue and consumer setting
Consumer code

package com.onlyk.api.rabbitmqapi.ttl;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

import java.util.Map;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 10:23
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class Consumer {
    public static void main(String[] args) throws Exception {

        //1 create a ConnectionFactory and configure it
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("140.143.242.67");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        //2 create a connection through a connection factory
        Connection connection = connectionFactory.newConnection();

        //3 create a Channel through connection
        Channel channel = connection.createChannel();

        //4 declare (create) a queue
        String queueName = "test001";
        channel.queueDeclare(queueName, true, false, false, null);

        //5 create consumers
        QueueingConsumer queueingConsumer = new QueueingConsumer(channel);

        //6 set Channel
        channel.basicConsume(queueName, true, queueingConsumer);

        while(true){
            //7 get message
            QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
            String msg = new String(delivery.getBody());
            System.err.println("Consumer end: " + msg);
            Map<String, Object> headers = delivery.getProperties().getHeaders();
            System.err.println("headers get my1 value: " + headers.get("my1"));
            //Envelope envelope = delivery.getEnvelope();
        }
    }
}

Production end code

package com.onlyk.api.rabbitmqapi.ttl;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 10:23
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class Procuder {
    public static void main(String[] args) throws Exception {
        //1 create a ConnectionFactory and configure it
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("140.143.242.67");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        //2 create a connection through a connection factory
        Connection connection = connectionFactory.newConnection();

        //3 create a Channel through connection
        Channel channel = connection.createChannel();

        Map<String, Object> headers = new HashMap<>();
        headers.put("my1", "111");
        headers.put("my2", "222");


        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .deliveryMode(2)
                .contentEncoding("UTF-8")
                .expiration("10000")
                .headers(headers)
                .build();

        //4 send data through Channel
        for(int i=0; i < 5; i++){
            String msg = "Hello RabbitMQ!";
            //1 exchange   2 routingKey
            channel.basicPublish("", "test001", properties, msg.getBytes());
        }
        //5 remember to close the connection
        channel.close();
        connection.close();
    }
}

Start the consumer side and start the production side

It's all right So what's this for? Only start the production end for trial (see the change of console)

Message auto death

Dead letter queue


Dead letter queue: DLX, dead letter exchange
With DLX, when a message becomes dead message in one queue, it can be re publish ed to another Exchange, which is DLX

There are several situations when a message becomes a dead letter

Message rejected (basic.reject/basic.nack) and request = false
 Message TTL expired
 Queue reaches maximum length

Characteristics of dead letter queue

DLX is also a normal Exchange, which is no different from general Exchange. It can be specified on any queue, in fact, it is to set the properties of a queue;
When there is dead message in the queue, RabbitMQ will automatically republish the message to the set Exchange, and then it will be routed to another queue;
You can listen to messages in this queue and process them accordingly. This feature can make up for the function of immediate parameter supported by RabbitMQ3.0;

Dead letter queue settings

First, you need to set Exchange and Queue of dead letter Queue, and then bind:
Exchange: dlx.exchange
Queue: dlx.queue
RoutingKey: #
Then we can declare the switch, queue and binding normally, but we need to add a parameter to the queue:
Arguments.put("x-dead-letter-exchange","dlx.exchange");
In this way, when the message expires, the request and the queue reach the maximum length, the message can be directly routed to the dead letter queue!

Consumer code

package com.onlyk.api.rabbitmqapi.dlx;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 10:52
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class Consumer {
    public static void main(String[] args) throws Exception {


        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("140.143.242.67");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        // This is a common switch, queue and route
        String exchangeName = "test_dlx_exchange";
        String routingKey = "dlx.#";
        String queueName = "test_dlx_queue";

        channel.exchangeDeclare(exchangeName, "topic", true, false, null);

        Map<String, Object> agruments = new HashMap<String, Object>();
        agruments.put("x-dead-letter-exchange", "dlx.exchange");
        //This arguments property, to be set on the declaration queue
        channel.queueDeclare(queueName, true, false, false, agruments);
        channel.queueBind(queueName, exchangeName, routingKey);

        //To declare the dead letter queue:
        channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);
        channel.queueDeclare("dlx.queue", true, false, false, null);
        channel.queueBind("dlx.queue", "dlx.exchange", "#");
        channel.basicConsume(queueName, true, new MyConsumer(channel));
    }
}

Production end code

package com.onlyk.api.rabbitmqapi.dlx;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * @author only Old K I speak for myself
 * @create 2020-03-04 10:52
 * @blogaddress https://blog.csdn.net/weixin_44255950
 */
public class Producer {
    public static void main(String[] args) throws Exception {

        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("140.143.242.67");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        String exchange = "test_dlx_exchange";
        String routingKey = "dlx.save";

        String msg = "Hello RabbitMQ DLX Message";

        for(int i =0; i<1; i ++){

            AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                    .deliveryMode(2)
                    .contentEncoding("UTF-8")
                    .expiration("10000")
                    .build();
            channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
        }

    }
}

Launch consumer and production

Only start the production side (pay attention to the changes.).. Dlx queue and dlx.queue)

Published 142 original articles, won praise 4, visited 7188
Private letter follow

Posted by silentweed on Tue, 03 Mar 2020 20:53:24 -0800