Привыкайте писать вместе! Это 11-й день моего участия в «Новом ежедневном плане Nuggets · Апрельское задание по обновлению», нажмите, чтобы просмотреть подробности мероприятия .
三、Рабочие очереди
3.1 Поочередное сообщение о распространении обучения
Несколько рабочих потоков на самом деле являются несколькими потребителями сообщений, также известными как процессоры.
эксклюзив: эксклюзив
Обучение ротации: сообщения в очереди будут равномерно распределены между несколькими потребителями для обработки.
**Основная идея очереди работ (она же очередь задач) состоит в том, чтобы избежать немедленного выполнения ресурсоемкой задачи и ожидания ее завершения. ** Вместо этого мы планируем выполнение задачи позже. Мы инкапсулируем задачу в виде сообщения и отправляем в очередь. Рабочий процесс, работающий в фоновом режиме, удалит задачу и в конечном итоге выполнит задание. При наличии нескольких рабочих потоков эти рабочие потоки будут обрабатывать эти задачи вместе.
3.1.1 Класс инструментов извлечения
package com.caq.rabbitmq.utils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class RabbitMqUtils {
//得到一个连接的 channel
public static Channel getChannel() throws Exception {
//创建一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.42.96");
factory.setUsername("admin");
factory.setPassword("123");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
return channel;
}
}
复制代码
3.1.2 Запуск двух рабочих потоков
package com.caq.rabbitmq.two;
import com.caq.rabbitmq.utils.RabbitMqUtils;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
/**
* 这是一个工作线程(相当于消费者)
*/
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(consumerTag + "消费者取消消费接口回调逻辑");
};
System.out.println("C2等待接受消息........");
channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
}
}
复制代码
3.1.3 Запуск потока отправки
package com.caq.rabbitmq.two;import com.caq.rabbitmq.utils.RabbitMqUtils;import com.rabbitmq.client.Channel;import java.util.Scanner;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("发送完成" + message); } }}
复制代码
Видно, что результаты обрабатываются по очереди
3.2 Ответ на сообщение
3.2.1 Концепция
После того, как потребитель завершает обработку бизнес-логики, он вручную возвращает подтверждение (уведомление), чтобы сообщить очереди, что обработка завершена, и очередь удаляет сообщение.
Потребителям может потребоваться некоторое время для завершения задачи, что произойдет, если один из потребителей обрабатывает длинную задачу и только частично завершает ее, а затем внезапно умирает. Как только RabbitMQ доставляет сообщение потребителю, он немедленно помечает сообщение для удаления. В этом случае внезапно умирает потребитель, и мы теряем сообщение, которое обрабатывали. и последующие сообщения, отправленные потребителю, потому что он не мог их получить.
Чтобы гарантировать, что сообщение не будет потеряно во время процесса отправки, rabbitmq вводит механизм ответа на сообщение . удалить сообщение.
3.2.2 Автоответ
消息发送后立即被认为已经传送成功,这种模式需要在高吞吐量和数据传输安全性方面做权衡,因为这种模式如果消息在接收到之前,消费者那边出现连接或者 channel 关闭,那么消息就丢失了,当然另一方面这种模式消费者那边可以传递过载的消息,没有对传递的消息数量进行限制, 当然这样有可能使得消费者这边由于接收太多还来不及处理的消息,导致这些消息的积压,最终使得内存耗尽,最终这些消费者线程被操作系统杀死,所以这种模式仅适用在消费者可以高效并以某种速率能够处理这些消息的情况下
3.2.3 消息应答的方法
手动应答:
A.Channel.basicAck(用于肯定确认)
RabbitMQ 已知道该消息并且成功的处理消息,可以将其丢弃了
B.Channel.basicNack(用于否定确认)
C.Channel.basicReject(用于否定确认)
与 Channel.basicNack 相比少一个参数
不处理该消息了直接拒绝,可以将其丢弃了
3.2.4 Multiple的解释
手动应答的两个否定确认方法相差的参数就是Multiple
multiple 的 true 和 false 代表不同意思
true 代表批量应答 channel 上未应答的消息
比如说 channel 上有传送 tag 的消息 5,6,7,8 当前 tag 是 8 那么此时5-8 的这些还未应答的消息都会被确认收到消息应答
false 同上面相比
只会应答 tag=8 的消息 5,6,7 这三个消息依然不会被确认收到消息应答
3.2.5 消息自动重新入队
** Если потребитель по какой-то причине теряет соединение (его канал закрыт, соединение закрыто или соединение TCP потеряно), из-за чего сообщение не отправляет подтверждение ACK, RabbitMQ будет знать, что сообщение не было полностью обработано. и повторно поставит его в очередь. Если другие потребители могут справиться с этим в этот момент, он вскоре перераспределит его другому потребителю. ** Таким образом, даже если потребитель время от времени умирает, вы можете быть уверены, что никакие сообщения не будут потеряны.
3.2.6 Ручной ответ на сообщение
потребители, как прежде
package com.caq.rabbitmq.three;import com.caq.rabbitmq.utils.RabbitMqUtils;import com.rabbitmq.client.Channel;import java.util.Scanner;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("UTF-8")); System.out.println("生产者发出消息" + message); } }}
复制代码
Потребитель 01
delivery.getEnvelope().getDeliveryTag() представляет тег сообщения
package com.caq.rabbitmq.three;import com.caq.rabbitmq.utils.RabbitMqUtils;import com.caq.rabbitmq.utils.SleepUtils;import com.rabbitmq.client.CancelCallback;import com.rabbitmq.client.Channel;import com.rabbitmq.client.DeliverCallback;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 = (consumerTag, delivery) -> { String message = new String(delivery.getBody()); SleepUtils.sleep(1); System.out.println("接收到消息:" + message); /** * 1.消息标记 tag * 2.是否批量应答未应答消息 */ channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); }; CancelCallback cancelCallback = (s) -> { System.out.println(s + "消费者取消消费接口回调逻辑"); }; //采用手动应答 boolean autoAck = false; channel.basicConsume(TASK_QUEUE_NAME, autoAck, deliverCallback, cancelCallback); }}
复制代码
Потребитель 02
Мы позволяем этому потребителю спать дольше, затем он будет очень медленно обрабатывать запрос сообщения, а затем отключать его.
Посмотрите, будут ли необработанные сообщения обрабатываться обычными потребителями.
package com.caq.rabbitmq.three;import com.caq.rabbitmq.utils.RabbitMqUtils;import com.caq.rabbitmq.utils.SleepUtils;import com.rabbitmq.client.CancelCallback;import com.rabbitmq.client.Channel;import com.rabbitmq.client.DeliverCallback;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 = (consumerTag, delivery) -> { String message = new String(delivery.getBody()); SleepUtils.sleep(30); System.out.println("接收到消息:" + message); /** * 1.消息标记 tag * 2.是否批量应答未应答消息 */ channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); }; CancelCallback cancelCallback = (s) -> { System.out.println(s + "消费者取消消费接口回调逻辑"); }; //采用手动应答 boolean autoAck = false; channel.basicConsume(TASK_QUEUE_NAME, autoAck, deliverCallback, cancelCallback); }}
复制代码
3.2.7 Демонстрация изображения
Эта часть должна быть пройдена до конца, иначе вы сможете понять только буквальное значение
Worker04 (потребитель) работает медленно после выключения. Сообщение будет обработано worker03 (потребителем). Это механизм автоматической постановки в очередь сообщений в ручных ответах.