RabbitMQ (HelloWord message response persistence unfair distribution prefetch value)


HelloWord

In the diagram below, "P" is our producer and "C" is our consumer. The middle box is a queue - RabbitMO. Represents the message buffer reserved by the user
insert image description here
Step 1: import dependencies

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.yc</groupId>
  <artifactId>rabbitmq-hello</artifactId>
  <version>1.0-SNAPSHOT</version>
  <!--指定jdk编译版本-->
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>8</source>
          <target>8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <!--rabbitmq依赖客户端-->
    <dependency>
      <groupId>com.rabbitmq</groupId>
      <artifactId>amqp-client</artifactId>
      <version>5.0.0</version>
    </dependency>
    <!--操作文件源的一个依赖-->
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.6</version>
    </dependency>
  </dependencies>
</project>

Step 2: Create a producer

//生产者:发消息
public class Producer {
    
    
    //队列名称
    public static final String QUEUE_NAME = "hello";

    //发消息
    public static void main(String[] args) throws IOException, TimeoutException {
    
    
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //工厂IP 连接RabbitMQ的队列
        factory.setHost("192.168.80.128");
        //用户名
        factory.setUsername("admin");
        //密码
        factory.setPassword("123");

        //创建连接
        Connection connection = factory.newConnection();
        //获取信道
        Channel channel = connection.createChannel();
        //生成一个队列
        /*
        * 1.队列名称
        * 2.队列里面的消息是否持久化(磁盘)默认情况消息存储在内存中
        * 3.该队列是否只供一个消费者进行消费,是否进行消息共享,true可以多个消费者消费false:只能一个消费者消费
         *4.是否自动剧除最后一个消贫者端开连接以后该队列是否自动鹏除 true自动鹏除false不自动翮除
         * 5.其它参数*/
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //发消息
        String message = "hello world";//初次使用

        /*发送一个消息
        * 1.发送到哪个交换机
        * 2.路由的Key值是哪个,本次是队列的名称
        * 3.其它参数信息
        * 4.发送消息的消息体*/
        channel.basicPublish("",QUEUE_NAME,null,message.getBytes());

        System.out.println("消息发送完毕");
    }
}

Step 3: Create a consumer

//消费者 接收消息的
public class Consumer {
    
    
    //队列名称
    public static final String QUEUE_NAME="hello";
    //接收消息
    public static void main(String[] args) throws IOException, TimeoutException {
    
    
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.80.128");
        factory.setUsername("admin");
        factory.setPassword("123");
        Connection connection = factory.newConnection();

        Channel channel = connection.createChannel();

        //声明
        DeliverCallback deliverCallback=(consumerTag, message)->{
    
    
            System.out.println(new String(message.getBody()));
        };

        //取消消息时的回调
        CancelCallback cancelCallback = consumerTag->{
    
    
            System.out.println("消费消息被中断");
        };
        //消费者消费消息
        //1.消费哪个队列
        //2.消费成功之后是否要自动应答
        //3.消费者未成功消费的回调
        //4.消费者取消消费的回调
        channel.basicConsume(QUEUE_NAME,true,deliverCallback,cancelCallback);
    }
}

work queue

insert image description here
Because in order to ensure that the same message is received by one of the worker threads, other jobs cannot be consumed. The
relationship between the three must be a competitive relationship .

Because
insert image description here
this part of the code is repeated back and forth, we can extract it. Connection Factory Utilities

public class RabbitMqUtils {
    
    
    //得到一个连接的channel
    public static Channel getChannel() throws Exception{
    
    
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.80.128");
        factory.setUsername("admin");
        factory.setPassword("123");
        Connection connection = factory.newConnection();

        Channel channel = connection.createChannel();

        return channel;
    }
}

Worker code

//这是一个工作线程(相当于之前的消费者)
public class Worker01 {
    
    
    //队列的名称
    public  static final String QUEUE_NAME = "hello";

    //接收消息
    public static void main(String[] args) throws Exception {
    
    
        Channel channel = RabbitMqUtils.getChannel();

        DeliverCallback deliverCallback=(consumerTag, message)->{
    
    
            System.out.println(new String(message.getBody()));
        };

        //取消消息时的回调
        CancelCallback cancelCallback = consumerTag->{
    
    
            System.out.println("消费消息被中断");
        };

        //消息的接收
        channel.basicConsume(QUEUE_NAME,true,deliverCallback,cancelCallback);
    }
}

Start two worker threads

insert image description here

Work queue (producer code)

public class Task01 {
    
    
    //队列名称
    public static final String QUEUE_NAME = "hello";

    //发送大量消息
    public static void main(String[] args) throws Exception{
    
    
        Channel channel = RabbitMqUtils.getChannel();

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //从控制台当中接受信息
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
    
    
            String message = scanner.next();
            channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
            System.out.println("发送消息完成");
        }
    }
}

work queue (result success)

insert image description here
insert image description here
insert image description here

message reply

We all know that it can take a while for a consumer to complete a task, what happens if one of the consumers processes a long task and only completes part of it and suddenly it dies?
In order to ensure that the message is not lost during the sending process, rabbitmq_ introduces a message response mechanism. The message response is: after the consumer receives the message and processes the message, it tells rabbitmq that it has been processed, and rabbitmq can delete the message.

auto answer

After the message is sent, it is considered to have been successfully delivered immediately. This mode needs to make a trade-off between high throughput and data transmission security, which makes the memory exhausted. Finally, these consumer threads are killed by the operating system. This mode is only applicable Used in situations where the consumer can process these messages efficiently and at a certain rate.

Manual Message Response

  • A.Channel.basicAck (for positive confirmation)
    RabbitMQ has known the message and successfully processed the message, it can be discarded
  • B.Channel. basicNack (for negative confirmation)
  • C.Channel.basicReject (for negative acknowledgment)
    has one less parameter than Channel.basicNack. Do not process the message and reject it directly, you can discard it

multiple explanation

The true and false of multiple represent different meanings.
true represents a batch response to unanswered messages on the channel
. For example, there are messages with tags 5, 6, 7, and 8 on the channel. The current tag is 8, so the 5-8 messages that have not been answered at this time Messages will be confirmed and received message response
false Compared with the above,
only messages 5, 6, and 7 with tag=8 will be responded to, and the message response will still not be confirmed.
insert image description here

Messages are automatically re-enqueued

If the consumer loses the connection for some reason (its channel is closed, the connection is closed, or the TCP connection is lost) such that the message does not send an ACK, RabbitMQ will understand that the message was not fully processed and will re-queue it. If other consumers can handle it at this point, it will quickly redistribute it to another consumer. This way, even if a consumer dies occasionally, you can be sure that no messages will be lost.
insert image description here

manual answer code

Message manual acknowledgment (producer)

public class Task2 {
    
    
    //队列名称
    public static final String TASK_QUEUE_NAME = "ack_queue";

    public static void main(String[] args) throws Exception {
    
    
        Channel channel = RabbitMqUtils.getChannel();

        //声明队列
        channel.queueDeclare(TASK_QUEUE_NAME,false,false,false,null);
        //从控制台中输入信息
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
    
    
            String message = scanner.next();
            channel.basicPublish("",TASK_QUEUE_NAME,null,message.getBytes());
            System.out.println("生产者发出消息:"+message);
        }
    }
}

Message manual reply (consumer)

public class Work03 {
    
    
    //队列名称
    public static final String TASK_QUEUE_NAME="ack_queue";

    //接收消息
    public static void main(String[] args) throws Exception {
    
    
        Channel channel = RabbitMqUtils.getChannel();
        System.out.println("C1等待接收消息处理时间较短");

        DeliverCallback deliverCallback = (consumeTag,message)->{
    
    
            //沉睡1S
            try {
    
    
                SleepUtils.sleep(1);
                System.out.println("接收到的消息:"+new String(message.getBody(),"UTF-8"));
                //手动应答
                //1.消息的标记 tag
                //2. 是否批量应答 false:不批量应答通信道中的消息 true:批量
                channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        };
        //采用手动应答
        boolean antoAck = false;
        channel.basicConsume(TASK_QUEUE_NAME,antoAck,deliverCallback,(consumerTag->{
    
    
            System.out.println(consumerTag+"消费者取消消费接口回调逻辑");
        }));
    }
}
public class Work04 {
    
    
    //队列名称
    public static final String TASK_QUEUE_NAME="ack_queue";

    //接收消息
    public static void main(String[] args) throws Exception {
    
    
        Channel channel = RabbitMqUtils.getChannel();
        System.out.println("C2等待接收消息处理时间较短");

        DeliverCallback deliverCallback = (consumeTag,message)->{
    
    
            //沉睡1S
            try {
    
    
                SleepUtils.sleep(30);
                System.out.println("接收到的消息:"+new String(message.getBody(),"UTF-8"));
                //手动应答
                //1.消息的标记 tag
                //2. 是否批量应答 false:不批量应答通信道中的消息 true:批量
                channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        };
        //采用手动应答
        boolean antoAck = false;
        channel.basicConsume(TASK_QUEUE_NAME,antoAck,deliverCallback,(consumerTag->{
    
    
            System.out.println(consumerTag+"消费者取消消费接口回调逻辑");
        }));
    }
}

Message manual acknowledgment (successful result)

insert image description here
insert image description here
insert image description here

RabbitMQ persistence

Just now we have seen how to deal with the situation that the task is not lost, but how to ensure that the message sent by the message producer is not lost when the Rabbi1MQJ service is stopped. By default when RahbitMQ exits or crashes for some reason it ignores queues and messages unless told not to. Ensuring that messages don't get lost requires two things: we need to mark both the queue and the messages as persistent.
When the queue is persistent
insert image description here
, D will be displayed here. At this time, even if the rabbitmq queue message is restarted, the message still exists, but it should be noted that if the previously declared queue is not persistent, the original queue needs to be deleted first, or a persistent one needs to be recreated. optimized queue, otherwise an error will occur

Queue implements persistence


public class Task2 {
    
    
    //队列名称
    public static final String TASK_QUEUE_NAME = "ack_queue";

    public static void main(String[] args) throws Exception {
    
    
        Channel channel = RabbitMqUtils.getChannel();

        //声明队列
        boolean durable = true;//需要让Queue进行持久化
        channel.queueDeclare(TASK_QUEUE_NAME,durable,false,false,null);
        //从控制台中输入信息
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
    
    
            String message = scanner.next();
            channel.basicPublish("",TASK_QUEUE_NAME,null,message.getBytes());
            System.out.println("生产者发出消息:"+message);
        }
    }
}

message persistence

The queue persistence we mentioned above can only guarantee that the queue will not be lost, but it cannot guarantee that the message will not be lost, so we also need to add a persistence to the message. To make the message persistent, we need to modify the code in the message producer, MessageProperties .PERSISTENT_TEXT_PLAIN Adding this attribute
insert image description here
to mark a message as persistent does not fully guarantee that the message will not be lost. Although it tells RabbitMQ to save the message to disk, there is still an interval when the message is just ready to be stored on disk but has not yet been stored, and the message is still cached. Nothing is actually written to disk at this point. Durability guarantees are not strong

unfair distribution

At the very beginning, we learned that RabbitMQ uses round-robin distribution to distribute messages, but this strategy is not very good in certain scenarios. For example, there are two consumers processing tasks, and one of them is consumer 1 processing tasks. The speed is very fast, but the processing speed of the other consumer 2 is very slow. At this time, we still adopt the method of training and distribution, and the consumer with the fast processing speed is idle for a large part of the time, while the consumer with the slow processing speed The author has been working, this distribution method is actually not very good in this case, but RabbitMQ does not know this situation and it still distributes it fairly.
In order to avoid this situation, we can set the parameter channel.basicQos(1);

insert image description here
when it is set to 1, it means that the current queue is unfairly distributed.
How to change the unfair distribution?

insert image description here
We only need to set such a parameter for the consumer

prefetch value

insert image description here
Here, if it is 0, it is a rotation training distribution, if it is 1, it is an unfair distribution, and if the value is greater than 1, it is a prefetch value. It can be specified in advance how many pieces of data are allocated to the queue.
insert image description here

Guess you like

Origin blog.csdn.net/m0_62434717/article/details/130140813