RabbitMQ Advanced --- Изображение не может быть успешно загружено

Практическое применение MQ

При использовании очередей сообщений возникает множество практических проблем, которые необходимо учитывать:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-Ec8z7cEw-1683378610380)(assets/image-20210718155003157.png)]

надежность сообщения

Когда сообщение отправляется от производителя на биржу, затем в очередь, а затем к потребителю, каковы возможности потери сообщения?

[Не удалось передать изображение по внешней ссылке, на исходном сайте может быть механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-NPkiyb9T-1683378610381)(assets/image-20210718155059371.png)]

теряется при отправке издателем

  • Сообщение, отправленное производителем, не было доставлено на биржу
  • Сообщение не попало в очередь после поступления на биржу

MQ не работает, очередь потеряет сообщение

Потребитель аварийно завершает работу без использования сообщения после получения сообщения

Для этих проблем RabbitMQ дает решения соответственно:

  • Механизм подтверждения производителя
  • mq настойчивость
  • Механизм подтверждения потребителя
  • Механизм повторной попытки отказа

Ниже мы демонстрируем каждый шаг на примере.

Сборка среды

МК

Обратитесь к предоставленным материалам для подготовки к уроку:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-Bl2lrDSy-1683378610382)(assets/image-20230202151016152.png)]

проект

Во-первых, импортируйте демонстрационный проект, предоставленный материалами предварительного класса:

[Передача изображения по внешней ссылке не удалась, исходный сайт может иметь механизм защиты от пиявки, рекомендуется сохранить изображение и загрузить его напрямую (img-rm0Z5tUp-1683378610382)(assets/image-20210718155328927.png)]

Структура проекта следующая:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-96wiSywv-1683378610382)(assets/image-20230205150421784.png)]

Подтверждение сообщения производителя

RabbitMQ предоставляет механизм подтверждения издателя, чтобы избежать потери сообщений, отправленных в MQ. Этот механизм должен назначать уникальный идентификатор (обычно идентификатор предприятия) каждому сообщению. После отправки сообщения в MQ отправителю будет возвращен результат, указывающий, успешно ли обработано сообщение.

Есть два способа вернуть результаты:

  • издатель-подтвердить, чтобы убедиться, что сообщение обмена от производителя к обмену отправлено успешно
    • Сообщение успешно доставлено на коммутатор и возвращает подтверждение
    • Сообщение не было доставлено на биржу, вернуть nack
  • издатель-возврат, когда сообщение не направляется в указанную очередь, сообщение может быть возвращено, а не отброшено
    • Сообщение было доставлено на биржу, но не направлено в очередь. Возвратите ACK и причину сбоя маршрутизации.

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-AvylNl19-1683378610383)(assets/image-20230205153150691.png)]

Примечание. Когда механизм подтверждения отправляет сообщение, необходимо установить глобально уникальный идентификатор для каждого сообщения, чтобы различать разные сообщения и избегать конфликтов подтверждения.

Изменить настройку

Сначала измените файл application.yml в службе издателя и добавьте следующее содержимое:

spring:
  rabbitmq:
    publisher-confirm-type: correlated
    publisher-returns: true
    template:
      mandatory: true

проиллюстрировать:

  • publish-confirm-type: Включить подтверждение издателя, здесь поддерживаются два типа:
    • simple: Синхронно ждать подтверждения результата до истечения времени ожидания
    • correlated: асинхронный обратный вызов, определите ConfirmCallback, этот ConfirmCallback будет вызван, когда MQ вернет результат.
  • publish-returns: Включить функцию публикации-возврата, которая также основана на механизме обратного вызова, но определяет функцию ReturnCallback.
  • template.mandatory: Включить ли принудительное сообщение при сбое маршрутизации. true для вызова ReturnCallback; false для прямого отбрасывания сообщения

Определить возврат

Каждый RabbitTemplate может настроить только один ReturnCallback, поэтому его необходимо настроить при загрузке проекта:

Измените службу издателя и добавьте ее:

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class CommonConfig implements ApplicationContextAware {
    
    
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
        // 获取RabbitTemplate
        RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
        // 设置ReturnCallback
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
    
    
            // 投递失败,记录日志
            log.error("交换机投递队列失败,应答码{},原因{},交换机{},路由键{},消息{}",
                     replyCode, replyText, exchange, routingKey, message.toString());
            // 如果有业务需要,可以重发消息
        });
    }
}

Определить ConfirmCallback

ConfirmCallback можно указать при отправке сообщения, потому что логика каждой бизнес-обработки подтверждения успеха или неудачи не обязательно одинакова.

В классе cn.itcast.mq.spring.SpringAmqpTest службы издателя определите метод модульного тестирования:

@Test
public void testSendMessage2SimpleQueue() throws InterruptedException {
    
    
    String exchange = "simple.direct";
    String routingKey = "simple";
    String message = "hello, spring amqp!";

    // 声明全局消息ID
    CorrelationData correlationData = new  CorrelationData(UUID.randomUUID().toString());
    // 添加callback
    correlationData.getFuture().addCallback(confirm -> {
    
    
        if (confirm.isAck()) {
    
    
            log.debug("消息投递交换机成功,ID:{}",correlationData.getId() );
        }else{
    
    
            log.error("消息投递交换机失败,ID:{},原因:{}",correlationData.getId(),confirm.getReason());
        }
    }, throwable -> {
    
    
        log.error("消息发送异常, ID:{}, 原因{}",correlationData.getId(),throwable.getMessage());
    });
    // 发送消息
    rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
}

сохранение сообщения

Производитель подтверждает, что сообщение может быть доставлено в очередь RabbitMQ, но после того, как сообщение будет отправлено в RabbitMQ, в случае внезапного простоя сообщение также может быть потеряно.

Чтобы гарантировать безопасное хранение сообщений в RabbitMQ, необходимо включить механизм сохранения сообщений.

  • переключать постоянство
  • постоянство очереди
  • сохранение сообщения

переключать постоянство

Переключатель в RabbitMQ по умолчанию является непостоянным и будет потерян после перезапуска mq.

В SpringAMQP постоянство переключателя можно указать с помощью кода:

@Bean
public DirectExchange simpleExchange(){
    
    
    // 三个参数:交换机名称、是否持久化、当没有queue与其绑定时是否自动删除
    return new DirectExchange("simple.direct", true, false);
}

Фактически, по умолчанию переключатели, объявленные Spring AMQP, являются постоянными.

Вы можете увидеть метку на постоянном переключателе в консоли RabbitMQ D:

[Передача изображения по внешней ссылке не удалась, исходный сайт может иметь механизм защиты от пиявки, рекомендуется сохранить изображение и загрузить его напрямую (img-rQ4m0gXc-1683378610383)(assets/image-20210718164412450.png)]

постоянство очереди

Очередь в RabbitMQ по умолчанию непостоянна и будет потеряна после перезапуска mq.

В SpringAMQP постоянство переключателя можно указать с помощью кода:

@Bean
public Queue simpleQueue(){
    
    
    // 使用QueueBuilder构建队列,durable就是持久化的
    return QueueBuilder.durable("simple.queue").build();
}

На самом деле по умолчанию очереди, объявленные Spring AMQP, являются постоянными.

Вы можете увидеть метку на постоянной очереди в консоли RabbitMQ D:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-euUrTOs8-1683378610384)(assets/image-20210718164729543.png)]

сохранение сообщения

При использовании SpringAMQP для отправки сообщений вы можете установить свойства сообщения (MessageProperties) и указать режим доставки:

  • 1: непостоянный
  • 2: Стойкость

Укажите с помощью кода Java:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-mNfKyLyI-1683378610384)(assets/image-20210718165100016.png)]

По умолчанию любое сообщение, отправляемое Spring AMQP, является постоянным, без его указания.

Подтверждение потребительского сообщения

RabbitMQ поддерживает механизм подтверждения потребителя, то есть потребитель может отправить квитанцию ​​подтверждения в MQ после обработки сообщения, и MQ удалит сообщение только после получения квитанции подтверждения.

SpringAMQP позволяет настроить три режима подтверждения:

  • manual: ручное подтверждение, вам нужно вызвать API для отправки подтверждения после окончания бизнес-кода.
  • auto (по умолчанию): автоматический ack, код прослушивателя отслеживается Spring на наличие исключений, если исключения нет, будет возвращен ack; если возникнет исключение, будет возвращен nack
  • none: отключить подтверждение, MQ предполагает, что потребитель успешно обработает сообщение после его получения, поэтому сообщение будет удалено сразу после доставки и сожжено после прочтения

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от пиявки, рекомендуется сохранить изображение и загрузить его напрямую (img-Eld3GdDr-1683378610384)(assets/image-20230205154414825.png)]

Как правило, мы можем использовать значение auto по умолчанию.

демонстрационный режим без

Измените файл application.yml службы потребителя и добавьте следующее содержимое:

spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: none # 关闭ack

Измените метод в классе SpringRabbitListener службы потребителя, чтобы имитировать исключение обработки сообщения:

@RabbitListener(queues = "simple.queue")
public void listenSimpleQueue(String msg) {
    
    
    log.info("消费者接收到simple.queue的消息:【{}】", msg);
    // 模拟异常
    System.out.println(1 / 0);
    log.debug("消息处理完成!");
}

Тест может обнаружить, что когда обработка сообщения выдает исключение, сообщение по-прежнему удаляется RabbitMQ.

демонстрационный автоматический режим

Снова измените механизм подтверждения на автоматический:

spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: auto # 关闭ack

Прервите точку в ненормальной позиции и снова отправьте сообщение.Когда программа застряла в точке останова, вы можете обнаружить, что статус сообщения не подтвержден (неопределенное состояние):

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-P6tXpjcn-1683378610385)(assets/image-20210718171705383.png)]

После создания исключения, поскольку Spring автоматически возвращает nack, сообщение возвращается в состояние Ready и не удаляется RabbitMQ:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-rrKw0C2J-1683378610385)(assets/image-20210718171759179.png)]

Механизм повторной попытки сбоя потребления

Когда у потребителя есть исключение, сообщение будет продолжать повторно помещаться в очередь (повторно входить в очередь) в очередь, а затем повторно отправляться потребителю, а затем снова исключение, снова помещать в очередь, бесконечный цикл, в результате чего обработка сообщения mq прерывается. парить, оказывая ненужное давление:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-RpkVwQsu-1683378610385)(assets/image-20210718172746378.png)]

Как это сделать?

локальная повторная попытка

Мы можем использовать механизм повторных попыток Spring, чтобы использовать локальные повторные попытки, когда в потребителе возникает исключение, вместо неограниченной повторной очереди в очередь mq.

Измените файл application.yml службы-потребителя и добавьте содержимое:

spring:
  rabbitmq:
    listener:
      simple:
        retry:
          enabled: true # 开启消费者失败重试
          initial-interval: 1000 # 初始的失败等待时长为1秒
          multiplier: 2 # 失败的等待时长倍数,下次等待时长 = multiplier * 上次等待时长
          max-attempts: 3 # 最大尝试次数
          stateless: true # true无状态;false有状态。如果业务中包含事务,这里改为false

Перезапустите службу потребителя и повторите предыдущий тест. Его можно найти:

  • После 3-х попыток SpringAMQP выдаст исключение AmqpRejectAndDontRequeueException, указывающее, что срабатывает локальная повторная попытка.
  • Проверьте консоль RabbitMQ и обнаружите, что сообщение было удалено, что указывает на то, что SpringAMQP вернул ack в конце, а mq удалил сообщение.

в заключение:

  • Когда включена локальная повторная попытка, если во время обработки сообщения возникает исключение, оно не будет запрашиваться в очереди, а будет повторно предпринято локально потребителем.
  • После достижения максимального количества попыток Spring вернет подтверждение, и сообщение будет удалено.

стратегия отказа

В предыдущем тесте по достижении максимального количества повторных попыток сообщение будет отброшено, что определяется внутренним механизмом Spring.

После включения режима повтора количество повторных попыток исчерпано. Если сообщение все еще не удается, для его обработки требуется интерфейс MessageRecovery. Он содержит три разных реализации:

  • RejectAndDontRequeueRecoverer: после исчерпания повторных попыток сразу отклонить и удалить сообщение. Это значение по умолчанию

  • ImmediateRequeueMessageRecoverer: после исчерпания повторных попыток возвращается nack, и сообщение повторно ставится в очередь.

  • RepublishMessageRecoverer: после исчерпания повторных попыток доставить сообщение об ошибке на указанный обмен

Более элегантное решение — RepublishMessageRecoverer: после сбоя сообщение будет доставлено в выделенную очередь, предназначенную для хранения сообщений об исключениях, а последующая обработка будет выполняться вручную.

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от личинга, рекомендуется сохранить изображение и загрузить его напрямую (img-fKapZCBF-1683378610386)(assets/1669046513902.png)]

1) Определить коммутатор и очередь для обработки ошибочных сообщений в службе потребителя.

@Bean
public DirectExchange errorMessageExchange() {
    
    
    return new DirectExchange("error.direct");
}

@Bean
public Queue errorQueue() {
    
    
    return new Queue("error.queue");
}

@Bean
public Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange) {
    
    
    return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with("error");
}

2) Определите RepublishMessageRecoverer в потребительской службе и отправьте его в указанную очередь коммутатора.

@Bean
public RepublishMessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){
    
    
    return new RepublishMessageRecoverer(rabbitTemplate, "error.direct", "error");
}

Полный код:

package cn.itcast.mq.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ErrorMessageConfig {
    
    


    // 1)在consumer服务中定义处理失败消息的交换机和队列
    @Bean
    public DirectExchange errorMessageExchange(){
    
    
        return new DirectExchange("error.direct");
    }
    @Bean
    public Queue errorQueue(){
    
    
        return new Queue("error.queue");
    }
    @Bean
    public Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){
    
    
        return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with("error");
    }

    // 2)定义一个RepublishMessageRecoverer,发送到指定交换机队列
    @Bean
    public RepublishMessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){
    
    
        return new RepublishMessageRecoverer(rabbitTemplate, "error.direct", "error");
    }
}

Подведем итог

Как обеспечить надежность сообщений RabbitMQ?

  • Включите механизм подтверждения производителя, чтобы гарантировать, что сообщение производителя может попасть в очередь.
  • Включите функцию сохраняемости, чтобы гарантировать, что сообщение не будет потеряно в очереди до того, как оно будет использовано.
  • Включите механизм подтверждения потребителя как автоматический, и Spring завершит подтверждение после подтверждения того, что сообщение успешно обработано.
  • Включите механизм повторных попыток при сбое потребителя и установите RepublishMessageRecoverer.После нескольких неудачных попыток сообщение будет доставлено на аварийный коммутатор и передано для ручной обработки.

обмен мертвыми письмами

Знакомство с переключателями недоставленных писем

Что такое обмен мертвыми письмами

Когда сообщение в очереди соответствует одному из следующих условий, оно может стать недоставленным:

  • Потребитель использует basic.reject или basic.nack, чтобы объявить о сбое потребления, а для параметра requeue сообщения установлено значение false.
  • Сообщение является сообщением с истекшим сроком действия, никто не потребляет после тайм-аута
  • Сообщение в очереди, которое нужно доставить, заполнено и не может быть доставлено

Если очередь, содержащая недоставленные письма, настроена с dead-letter-exchangeатрибутами и указан коммутатор, то недоставленные письма в очереди будут доставляться на этот коммутатор, и этот коммутатор называется Обмен недоставленными письмами (сокращенно DLX).

Как показано на рисунке, сообщение отвергается потребителем и становится недоставленным:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-U7CSANmE-1683378610386)(assets/image-20210718174328383.png)]

Поскольку simple.queue привязан к обмену недоставленными сообщениями dl.direct, недоставленные письма будут доставлены на этот обмен:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-g0apqVCM-1683378610387)(assets/image-20210718174416160.png)]

Если переключатель недоставленных сообщений также привязан к очереди, сообщение в конечном итоге попадет в очередь, в которой хранится недоставленное письмо:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-WsVbpNoX-1683378610387)(assets/image-20210718174506856.png)]

Кроме того, когда очередь доставляет недоставленное письмо на обмен недоставленными сообщениями, она должна знать две части информации:

  • имя переключателя мертвой буквы
  • RoutingKey привязан к обмену недоставленными сообщениями и очереди недоставленных сообщений

Только таким образом мы можем гарантировать, что доставленное сообщение может достичь обмена недоставленными сообщениями и быть правильно перенаправлено в очередь недоставленных сообщений.

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-xy7BXX4z-1683378610387)(assets/1669047077591.png)]

Подведем итог

Какое сообщение становится мертвой буквой?

  • Сообщение отклонено потребителем или возвращено nack
  • Время ожидания сообщения истекло и не используется
  • очередь заполнена

Как привязать обмен недоставленными письмами к очереди?

  • Установите атрибут обмена недоставленными сообщениями в очередь и укажите обмен

  • Установите атрибут ключа маршрутизации недоставленных сообщений для очереди и задайте значение RoutingKey коммутатора недоставленных сообщений и очереди недоставленных сообщений.

время жизни

TTL, то есть Time-To-Live. Если TTL сообщения в очереди еще не израсходовано, оно станет мертвой буквой.Существует два случая тайм-аута ttl:

  • Само сообщение устанавливает тайм-аут
  • Очередь, в которой находится сообщение, имеет установленное время ожидания.

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-1pbsqASI-1683378610388)(assets/image-20230207181455536.png)]

Переключатель недоставленных сообщений, который получает недоставленные сообщения с истекшим временем ожидания.

В SpringRabbitListener службы потребителя определите нового потребителя и объявите переключатель недоставленных сообщений и очередь недоставленных сообщений:

@RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "dl.queue"),
    exchange = @Exchange(name = "dl.direct"),
    key = "dl"
))
public void listenDlQueue(String msg){
    
    
    log.info("接收到 dl.queue的延迟消息:{}", msg);
}

Объявить очередь и указать TTL

Чтобы задать таймаут для очереди, нужно настроить атрибут x-message-ttl при объявлении очереди:

package cn.itcast.mq.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TTLMessageConfig {
    
    

    // 给队列设置超时时间
    @Bean
    public Queue ttlQueue() {
    
    
        return QueueBuilder.durable("ttl.queue") // 指定队列名称,并持久化
                .ttl(10000) // 设置队列的超时时间,10秒
                .deadLetterExchange("dl.direct") // 指定死信交换机
                .deadLetterRoutingKey("dl") // 指定路由key
                .build();
    }

    // 声明交换机,将ttl与交换机绑定
    @Bean
    public DirectExchange ttlExchange() {
    
    
        return new DirectExchange("ttl.direct");
    }

    @Bean
    public Binding ttlBinding(DirectExchange ttlExchange, Queue ttlQueue) {
    
    
        return BindingBuilder.bind(ttlQueue).to(ttlExchange).with("ttl");
    }
}

Отправить сообщение без указания TTL

@Test
public void testTTLQueue() {
    
    
    // 创建消息
    String message = "hello, ttl queue";
    // 消息ID,需要封装到CorrelationData中
    CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
    // 发送消息
    rabbitTemplate.convertAndSend("ttl.direct", "ttl", message, correlationData);
    // 记录日志
    log.debug("发送消息成功");
}

Журнал отправленных сообщений:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от пиявки, рекомендуется сохранить изображение и загрузить его напрямую (img-OkOfJZ0s-1683378610388)(assets/image-20210718191657478.png)]

Посмотреть журнал полученных сообщений:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-BpDA1CmS-1683378610388)(assets/image-20210718191738706.png)]

Потому что значение TTL очереди составляет 10000 мс, что составляет 10 секунд. Вы можете видеть, что разница во времени между отправкой и получением сообщения составляет ровно 10 секунд.

Отправить сообщение, указать TTL

При отправке сообщения также можно указать TTL:

@Test
public void testTTLMsg() {
    
    
    // 创建消息
    Message message = MessageBuilder
        .withBody("hello, ttl message".getBytes(StandardCharsets.UTF_8))
        .setExpiration("5000")
        .build();
    // 消息ID,需要封装到CorrelationData中
    CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
    // 发送消息
    rabbitTemplate.convertAndSend("ttl.direct", "ttl", message, correlationData);
    log.debug("发送消息成功");
}

Посмотреть журнал отправленных сообщений:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-tBSLBC10-1683378610389)(assets/image-20210718191939140.png)]

Журнал получения сообщений:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-PRXqcIzD-1683378610389)(assets/image-20210718192004662.png)]

На этот раз задержка между отправкой и получением составила всего 5 секунд. Обратите внимание, что когда TTL установлен как для очереди, так и для сообщения, любое сообщение с истекшим сроком действия станет недоставленным письмом.

Подведем итог

Каковы два способа тайм-аута сообщения?

  • Установите атрибут ttl для очереди, и сообщения, которые превышают время ttl после попадания в очередь, становятся мертвыми буквами
  • Установите атрибут ttl для сообщения, и очередь станет мертвой буквой после получения сообщения, превышающего время ttl.
  • Когда они сосуществуют, более короткий ttl имеет преимущественную силу.

Как реализовать, что потребитель получает сообщение через 20 секунд после отправки сообщения?

  • Укажите обмен недоставленными сообщениями для очереди назначения сообщения
  • Привяжите очередь, прослушиваемую потребителем, к обмену недоставленными сообщениями.
  • При отправке сообщения установите период ожидания для сообщения на 20 секунд.

очередь задержки

Используя TTL в сочетании с переключателями недоставленных сообщений, мы реализуем эффект, заключающийся в том, что потребители задерживают получение сообщений после отправки сообщений. Этот режим сообщений называется режимом очереди с задержкой (Delay Queue).

Примеры использования очередей с задержкой включают:

  • Задержка отправки смс
  • Пользователь размещает заказ, если пользователь не оплатил в течение 15 минут, он будет автоматически отменен
  • Запланируйте рабочую встречу и автоматически уведомите всех участников через 20 минут

Установите плагин DelayExchange.

Поскольку требований к очередям задержки очень много, RabbitMQ официально выпустил подключаемый модуль, изначально поддерживающий эффекты очереди задержки.

Подробный процесс установки см. в Разделе 2 «Установка подключаемого модуля DelayExchange» в предварительном документе «RabbitMQ Deployment Guide.md»:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-micCBPNx-1683378610389)(assets/image-20210718193409812.png)]

Используйте DelayExchange

Использование плагина также очень простое: объявите переключатель, тип переключателя может быть любым, просто установите для атрибута delayed значение true, а затем объявите очередь для его привязки.

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-y3JaIQHX-1683378610390)(assets/image-20230205170335009.png)]

Объявить переключатель DelayExchange

На основе аннотаций:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от похищения, рекомендуется сохранить изображение и загрузить его напрямую (img-eLXhyAQT-1683378610390)(assets/image-20210718193747649.png)]

@RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "delay.queue", durable = "true"),
    exchange = @Exchange(name = "delay.direct", delayed = "true"),
    key = "delay"
))
public void listenDelayedQueue(String msg) {
    
    
    log.info("接收到 delay.queue的延迟消息{}", msg);
}

Он также может быть основан на @Bean:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-ADroKETN-1683378610390)(assets/image-20210718193831076.png)]

Отправить сообщение

При отправке сообщения не забудьте указать атрибут x-delay, чтобы указать время задержки:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-3R2bk2xQ-1683378610391)(assets/image-20210718193917009.png)]

@Test
public void testDelayedMsg()throws Exception{
    
    
    // 创建消息
    Message message = MessageBuilder
        .withBody("hello, delayed message".getBytes(StandardCharsets.UTF_8))
        .setHeader("x-delay",10000)
        .build();
    // 消息ID,需要封装到CorrelationData中
    CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
    // 发送消息
    rabbitTemplate.convertAndSend("delay.direct", "delay", message, correlationData);
    log.debug("发送消息成功");
}

Обработка сообщений обратного вызова

При обработке задержанных данных очереди отправитель получит сообщение об ошибке, которое не было правильно доставлено в очередь (глава 2.2.2 в разделе «Надежность сообщений»), мы можем добавить суждение в обратный вызов, чтобы сделать это правильно

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-F1SVsUJC-1683378610391)(assets/image-20230205170641325.png)]

Подведем итог

Каковы шаги для использования плагина очереди задержки?

  • Объявите переключатель и добавьте атрибуту delayed значение true
  • При отправке сообщения добавить заголовок x-delay, значением является время ожидания

ленивая очередь

проблема накопления сообщений

Когда скорость, с которой производители отправляют сообщения, превышает скорость, с которой потребители могут обрабатывать сообщения, сообщения в очереди будут накапливаться до тех пор, пока очередь не сохранит сообщения до верхнего предела. Сообщения, отправленные позже, станут мертвыми буквами и могут быть отброшены, это проблема накопления сообщений.

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-OC1PalZu-1683378610391)(assets/image-20210718194040498.png)]

Есть два способа решить проблему накопления сообщений:

  • Добавьте больше потребителей и увеличьте скорость потребления. Это режим рабочей очереди, о котором мы говорили ранее.
  • Расширьте объем очереди и увеличьте предел стека

Очевидно, что для увеличения емкости очереди нельзя хранить сообщения в памяти.

ленивая очередь

Начиная с версии 3.6.0 RabbitMQ добавлено понятие Lazy Queues, то есть ленивых очередей. Характеристики ленивой очереди следующие:

  • После получения сообщения сохраните его непосредственно на диске, а не в памяти.
  • Потребители только читают с диска и загружают их в память, когда хотят потреблять сообщения.
  • Поддержка миллионов хранилищ сообщений

На основе объявления @Bean

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-CKUTTYUI-1683378610391)(assets/image-20210718194522223.png)]

package cn.itcast.mq.config;

import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LazyMessageConfig {
    
    
    @Bean
    public Queue lazyQueue() {
    
    
        return QueueBuilder
                .durable("lazy.queue")
                .lazy()
                .build();
    }

    @Bean
    public Queue normalQueue() {
    
    
        return QueueBuilder
                .durable("normal.queue")
                .build();
    }
}

На основе объявления @RabbitListener

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-Eeq3J5yo-1683378610392)(assets/image-20210718194539054.png)]

Отправляйте сообщения 10W

@Test
public void testLazyMsg()throws Exception{
    
    
    for (int i = 0; i <100000 ; i++) {
    
    
        // 创建消息
        Message message = MessageBuilder
            .withBody("hello, lazy message".getBytes(StandardCharsets.UTF_8))
            .build();
        // 消息ID,需要封装到CorrelationData中
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        // 发送消息
        rabbitTemplate.convertAndSend("lazy.queue", message, correlationData);
    }
    log.debug("发送消息成功");
}


@Test
public void testNormalMsg()throws Exception{
    
    
    for (int i = 0; i <100000 ; i++) {
    
    
        // 创建消息
        Message message = MessageBuilder
            .withBody("hello, normal message".getBytes(StandardCharsets.UTF_8))
            .build();
        // 消息ID,需要封装到CorrelationData中
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        // 发送消息
        rabbitTemplate.convertAndSend("normal.queue", message, correlationData);
    }
    log.debug("发送消息成功");
}

Подведем итог

Решение проблемы накопления сообщений?

  • Привяжите несколько потребителей к очереди, чтобы увеличить скорость потребления
  • Используя ленивые очереди, вы можете хранить больше сообщений в mq.

Каковы преимущества ленивых очередей?

  • В зависимости от дискового пространства верхний предел сообщений высок.
  • Периодического вывода страниц нет, производительность относительно стабильна.

Каковы недостатки ленивых очередей?

  • В зависимости от дискового пространства, своевременность сообщений будет снижена
  • Производительность ограничена дисковым вводом-выводом

Кластер MQ

кластерная классификация

RabbitMQ написан на основе языка Erlang, а Erlang — это язык, ориентированный на параллелизм, естественно поддерживающий кластерный режим.

Кластер RabbitMQ имеет два режима:

  • Обычный кластер : это распределенный кластер, который распределяет очереди на каждый узел кластера, тем самым улучшая возможности параллелизма всего кластера.

  • Зеркальный кластер : это кластер master-slave.На основе обычных кластеров добавлена ​​функция резервного копирования master-slave для повышения доступности данных кластера.

Хотя зеркальный кластер поддерживает ведущий-ведомый, синхронизация ведущий-ведомый не является строго согласованной, и в некоторых случаях может возникнуть риск потери данных.

Поэтому после RabbitMQ версии 3.8 была введена новая функция: очередь арбитража на замену зеркальному кластеру, а нижний уровень использует протокол Raft для обеспечения согласованности данных между мастером и слейвом.

нормальный кластер

Структура и характеристики кластера

Обычные кластеры или классические кластеры имеют следующие характеристики:

  • Она будет совместно использоваться всеми узлами в кластере 部分数据, включая: метаинформацию о коммутаторе и очереди. Сообщения в очереди не включаются.
  • При доступе к узлу в кластере, если очередь не находится на узле, она будет передана от узла, где находятся данные, к текущему узлу и возвращена
  • Если узел, на котором находится очередь, выйдет из строя, сообщения в очереди будут потеряны.

Структура представлена ​​на рисунке:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-Dy18cyPS-1683378610392)(assets/image-20210718220843323.png)]

изображение-20230207205509399

развертывать

Ссылка: "Руководство по развертыванию RabbitMQ.md"

зеркальный кластер

Структура и характеристики кластера

Зеркальный кластер: сущностью является режим master-slave со следующими характеристиками:

  • Узел, создающий очередь, называется основным узлом очереди, а другие узлы, на которые выполняется резервное копирование, называются зеркальными узлами очереди.
  • Коммутаторы, очереди и сообщения в очередях будут синхронно копироваться между зеркальными узлами каждого mq.
  • Главный узел очереди может быть зеркальным узлом другой очереди.
  • Все операции завершаются мастер-узлом, а затем синхронизируются с зеркальным узлом.
  • После того, как мастер выйдет из строя, зеркальный узел будет заменен новым мастером.

Структура представлена ​​на рисунке:

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи ссылок, рекомендуется сохранить изображение и загрузить его напрямую (img-PPRqtm0v-1683378610392)(assets/image-20210718221039542.png)]

[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от кражи, рекомендуется сохранить изображение и загрузить его напрямую (img-yK5cefhc-1683378610393)(assets/image-20230207205541962.png)]

.развертывать

Ссылка: "Руководство по развертыванию RabbitMQ.md"

Арбитражная очередь

характеристики кластера

Арбитражная очередь: Арбитражная очередь — это новая функция, доступная после версии 3.8. Она используется для замены зеркальной очереди и имеет следующие характеристики:

  • Как и зеркальная очередь, это режим ведущий-ведомый и поддерживает синхронизацию данных ведущий-ведомый.
  • Очень прост в использовании, нет сложной настройки
  • Синхронизация master-slave на основе протокола Raft, строгая согласованность

развертывать

Ссылка: "Руководство по развертыванию RabbitMQ.md"

Код Java для создания очереди кворума

@Bean
public Queue quorumQueue() {
    
    
    return QueueBuilder
        .durable("quorum.queue") // 持久化
        .quorum() // 仲裁队列
        .build();
}

SpringAMQP подключается к кластеру MQ

Обратите внимание, что здесь используется адрес вместо хоста и порта.

spring:
  rabbitmq:
    addresses: 192.168.136.130:8071,192.168.136.130:8072, 192.168.136.130:8073
    username: itcast
    password: 123321
    virtual-host: /

нг" alt = "изображение-20230207205509399" стиль = "увеличение: 77%;" />

развертывать

Ссылка: "Руководство по развертыванию RabbitMQ.md"

зеркальный кластер

Структура и характеристики кластера

Зеркальный кластер: сущностью является режим master-slave со следующими характеристиками:

  • Узел, создающий очередь, называется основным узлом очереди, а другие узлы, на которые выполняется резервное копирование, называются зеркальными узлами очереди.
  • Коммутаторы, очереди и сообщения в очередях будут синхронно копироваться между зеркальными узлами каждого mq.
  • Главный узел очереди может быть зеркальным узлом другой очереди.
  • Все операции завершаются мастер-узлом, а затем синхронизируются с зеркальным узлом.
  • После того, как мастер выйдет из строя, зеркальный узел будет заменен новым мастером.

Структура представлена ​​на рисунке:

[Передача изображения по внешней ссылке...(img-PPRqtm0v-1683378610392)]

[Передача изображения по внешней ссылке...(img-yK5cefhc-1683378610393)]

.развертывать

Ссылка: "Руководство по развертыванию RabbitMQ.md"

Арбитражная очередь

характеристики кластера

Арбитражная очередь: Арбитражная очередь — это новая функция, доступная после версии 3.8. Она используется для замены зеркальной очереди и имеет следующие характеристики:

  • Как и зеркальная очередь, это режим ведущий-ведомый и поддерживает синхронизацию данных ведущий-ведомый.
  • Очень прост в использовании, нет сложной настройки
  • Синхронизация master-slave на основе протокола Raft, строгая согласованность

развертывать

Справочные материалы перед занятием: "Руководство по развертыванию RabbitMQ.md"

Код Java для создания очереди кворума

@Bean
public Queue quorumQueue() {
    
    
    return QueueBuilder
        .durable("quorum.queue") // 持久化
        .quorum() // 仲裁队列
        .build();
}

SpringAMQP подключается к кластеру MQ

Обратите внимание, что здесь используется адрес вместо хоста и порта.

spring:
  rabbitmq:
    addresses: 192.168.136.130:8071,192.168.136.130:8072, 192.168.136.130:8073
    username: itcast
    password: 123321
    virtual-host: /

Guess you like

Origin blog.csdn.net/qq_46020806/article/details/130535668