Aprendizaje de microservicios Capítulo avanzado [5] Servicio de comunicación asíncrona (características avanzadas de Rabbitmq)

Directorio de artículos

  • Algunos problemas comunes de MQ
    inserte la descripción de la imagen aquí

Preparación del entorno: implementación de MQ [entorno acoplable]

  1. espejo de descarga
    docker pull rabbitmq:3.8-management
    
    inserte la descripción de la imagen aquí
  2. Instalar MQ
    docker run \
     -e RABBITMQ_DEFAULT_USER=itcast \
     -e RABBITMQ_DEFAULT_PASS=123321 \
     -v mq-plugins:/plugins \
     --name mq1 \
     --hostname mq1 \
     -p 15672:15672 \
     -p 5672:5672 \
     -d \
     rabbitmq:3.8-management
    
  3. prueba
    [root@kongyue ~]# docker ps
    CONTAINER ID   IMAGE                     COMMAND                   CREATED          STATUS          PORTS                                                                                                                                                 NAMES
    7eea857604d3   rabbitmq:3.8-management   "docker-entrypoint.s…"   17 seconds ago   Up 16 seconds   4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, :::5672->5672/tcp, 15671/tcp, 15691-15692/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp, :::15672->15672/tcp   mq1
    

confiabilidad del mensaje

  • Desde el envío del mensaje hasta su recepción por parte del consumidor, gestionará múltiples procesos. Cada uno de estos pasos puede conducir a la pérdida de mensajes, las razones comunes de pérdida incluyen:
    inserte la descripción de la imagen aquí
  • Para estos problemas, RabbitMQ da soluciones respectivamente:
    • Mecanismo de Confirmación del Productor
    • persistencia mq
    • Mecanismo de Confirmación del Consumidor
    • Mecanismo de reintento fallido

Acuse de recibo del mensaje del productor

RabbitMQ proporciona un mecanismo de confirmación del editor para evitar la pérdida de mensajes enviados a MQ. Este mecanismo debe asignar una identificación única a cada mensaje. Después de enviar el mensaje a MQ, se devolverá un resultado al remitente que indica si el mensaje se procesó correctamente. Hay dos formas de devolver resultados:

  • Publisher-confirm, el remitente confirma
    • El mensaje se entrega con éxito al conmutador y devuelve un acuse de recibo.
    • El mensaje no se entregó al intercambio , devuelva nack
    • Ocurrió una excepción durante el envío del mensaje y no se recibió ningún recibo
  • editor-devolución, recibo del remitente
    • Los mensajes se entregan a los intercambios, pero no se enrutan a las colas. Devuelve ACK y el motivo del error de enrutamiento.
      inserte la descripción de la imagen aquí
  • Nota: Cuando el mecanismo de confirmación envía un mensaje, es necesario establecer una identificación global única para cada mensaje para distinguir los diferentes mensajes y evitar conflictos de confirmación.

configuración del proyecto

  • Primero, modifique el archivo application.yml en el servicio productor y agregue el siguiente contenido:
    spring:
      rabbitmq:
        publisher-confirm-type: correlated #生产者确认类型
        publisher-returns: true #开启publish-return功能
        template:
          mandatory: true #定义消息路由失败时的策略
    
    • publish-confirm-type: habilite la confirmación del editor, aquí admite dos tipos:
      • simple: Espere sincrónicamente el resultado de la confirmación hasta que se agote el tiempo de espera
      • correlated: Devolución de llamada asincrónica, defina ConfirmCallback, esta ConfirmCallback se devolverá cuando MQ devuelva el resultado
    • publish-returns: habilite la función de devolución de publicación, que también se basa en el mecanismo de devolución de llamada, pero define la función ReturnCallback
    • template.mandatory: define la política cuando falla el enrutamiento de mensajes. verdadero, llamar a ReturnCallback; falso: descartar el mensaje directamente

Definir Devolución de llamada y Confirmar devolución de llamada

  • El mensaje se envía con éxito al intercambio, pero no se enruta a la cola, llame a ReturnCallback

  1. Cada RabbitTemplate solo puede configurar un ReturnCallback, por lo que debe configurarse cuando se carga el proyecto:
  • Modifique el servicio del productor y agregue uno:
    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.info("消息发送失败,应答码{},原因{},交换机{},路由键{},消息{}",
                         replyCode, replyText, exchange, routingKey, message.toString());
                // 如果有业务需要,可以重发消息
            });
        }
    }
    

  1. ConfirmCallback se puede especificar al enviar un mensaje, ya que la lógica de cada procesamiento empresarial confirma el éxito o el fracaso no es necesariamente la misma.
  • Defina un método de prueba unitario:
    public void testSendMessage2SimpleQueue() throws InterruptedException {
          
          
        // 1.消息体
        String message = "hello, spring amqp!";
        // 2.全局唯一的消息ID,需要封装到CorrelationData中
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        // 3.添加callback
        correlationData.getFuture().addCallback(
            result -> {
          
          
                if(result.isAck()){
          
          
                    // 3.1.ack,消息成功
                    log.debug("消息发送成功, ID:{}", correlationData.getId());
                }else{
          
          
                    // 3.2.nack,消息失败
                    log.error("消息发送失败, ID:{}, 原因{}",correlationData.getId(), result.getReason());
                }
            },
            ex -> log.error("消息发送异常, ID:{}, 原因{}",correlationData.getId(),ex.getMessage())
        );
        // 4.发送消息
        rabbitTemplate.convertAndSend("task.direct", "task", message, correlationData);
    
        // 休眠一会儿,等待ack回执
        Thread.sleep(2000);
    }
    

persistencia del mensaje

  • MQ almacena los mensajes en la memoria de forma predeterminada. Para garantizar que los mensajes se almacenen de forma segura en RabbitMQ, el mecanismo de persistencia de mensajes debe estar habilitado para garantizar que los mensajes almacenados en caché en MQ no se pierdan.
    • cambiar la persistencia
    • persistencia de la cola
    • persistencia del mensaje

cambiar la persistencia

  • En SpringAMQP, la persistencia del cambio se puede especificar mediante código:
    @Bean
    public DirectExchange simpleExchange(){
          
          
        // 三个参数:交换机名称、是否持久化、当没有queue与其绑定时是否自动删除
        return new DirectExchange("simple.direct", true, false);
    }
    
    • De forma predeterminada, los conmutadores declarados por Spring AMQP son persistentes.
  • En la consola de RabbitMQ, puede ver la marca en el interruptor persistente D:inserte la descripción de la imagen aquí

persistencia de la cola

  • En SpringAMQP, la persistencia del cambio se puede especificar mediante código:
    @Bean
    public Queue simpleQueue(){
          
          
        // 使用QueueBuilder构建队列,durable就是持久化的
        return QueueBuilder.durable("simple.queue").build();
    }
    
    • De forma predeterminada, las colas declaradas por Spring AMQP son persistentes.
  • La consola RabbitMQ ve que la cola persistente se Dmarcará con:
    inserte la descripción de la imagen aquí

persistencia del mensaje

Al usar SpringAMQP para enviar mensajes, puede configurar las propiedades del mensaje (MessageProperties) y especificar el modo de entrega:

  • 1: no persistente
  • 2: Persistencia
 @Test
    public void testDuarbleMessage() {
    
    
        //创建消息
        Message message = MessageBuilder.withBody("hello, ttl queue".getBytes(StandardCharsets.UTF_8))
                .setDeliveryMode(MessageDeliveryMode.PERSISTENT)
                .build();
        //消息ID,需要封装到需要封装到CorrelationData中
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        //发送消息
        rabbitTemplate.convertAndSend("simple.queue",message,correlationData);
        log.debug("发送消息成功!");
    }
  • Por defecto, cualquier mensaje enviado por Spring AMQP es persistente, sin especificarlo.

Mensaje de confirmación del consumidor

  • RabbitMQ es un mecanismo que se quema después de leer RabbitMQ confirma que el mensaje se eliminará inmediatamente después de ser consumido por el consumidor. RabbitMQ admite el mecanismo de confirmación del consumidor, es decir, el consumidor puede enviar un acuse de recibo a MQ después de procesar el mensaje, y MQ eliminará el mensaje después de recibir el acuse de recibo.

Imagina este escenario:

  • 1) RabbitMQ entrega mensajes a los consumidores
  • 2) Después de que el consumidor recibe el mensaje, devuelve ACK a RabbitMQ
  • 3) Mensaje de eliminación de RabbitMQ
  • 4) El consumidor está caído y el mensaje no ha sido procesado
    , de esta forma se pierde el mensaje. Por lo tanto, el momento en que el consumidor devuelve el ACK es muy importante.

SpringAMQP permite la configuración de tres modos de confirmación:

  • manual: acuse de recibo manual, debe llamar a la API para enviar acuse de recibo después de que finalice el código comercial.
  • auto: acuse de recibo automático, el código de escucha es monitoreado por Spring para ver si hay una excepción, si no hay excepción, devolverá un acuse de recibo; si se lanza una excepción, devolverá un nack
  • none: close ack, MQ asume que el consumidor procesará con éxito el mensaje después de recibirlo, por lo que el mensaje se eliminará inmediatamente después de la entrega

De esto podemos ver:

  • En modo ninguno, la entrega de mensajes no es confiable y puede perderse
  • El modo automático es similar al mecanismo de transacción. Cuando ocurre una excepción, devuelve nack y el mensaje se retrotrae a mq; si no hay excepción, devuelve ack
  • manual: De acuerdo con la situación comercial, juzgue cuándo el reconocimiento
    debe ser normal. Todos usamos el auto predeterminado.

demostración de ningún modo

  • Modificar el mecanismo de confirmación:
    spring:
      rabbitmq:
        listener:
          simple:
            acknowledge-mode: auto # 关闭ack
    
  • Rompe el punto en la posición anormal y envía el mensaje nuevamente. Cuando el programa se atasca en el punto de interrupción, puede encontrar que el estado del mensaje no es correcto (estado indeterminado):
    inserte la descripción de la imagen aquí
  • Después de que se lanza una excepción, porque Spring devolverá automáticamente nack, el mensaje vuelve al estado Listo y RabbitMQ no lo elimina:
    inserte la descripción de la imagen aquí

Mecanismo de reintento de fallo de consumo

  • Cuando el consumidor tiene una excepción, el mensaje continuará volviendo a la cola (volver a ingresar a la cola) a la cola, y luego se volverá a enviar al consumidor, y luego la excepción nuevamente, volverá a la cola, bucle infinito, lo que hará que el procesamiento del mensaje de mq a elevarse, ejerciendo una presión innecesaria:
    inserte la descripción de la imagen aquí inserte la descripción de la imagen aquí

reintento local

  • Use el mecanismo de reintento de Spring para usar el reintento local cuando el consumidor es anormal, en lugar de una reposición ilimitada a la cola mq.
  • Modifique el archivo application.yml del servicio al consumidor y agregue el contenido:
    spring:
      rabbitmq:
        listener:
          simple:
            retry:
              enabled: true # 开启消费者失败重试
              initial-interval: 1000 # 初识的失败等待时长为1秒
              multiplier: 1 # 失败的等待时长倍数,下次等待时长 = multiplier * last-interval
              max-attempts: 3 # 最大重试次数
              stateless: true # true无状态;false有状态。如果业务中包含事务,这里改为false
    
  • Reinicie el servicio al consumidor y repita la prueba:
    • SpringAMQP lanzará una excepción AmqpRejectAndDontRequeueException
    • Verifique la consola de RabbitMQ y descubra que el mensaje se eliminó, lo que indica que el último SpringAMQP devuelve el reconocimiento y mq elimina el mensaje
      inserte la descripción de la imagen aquí

en conclusión:

  • Cuando el reintento local está habilitado, si se lanza una excepción durante el procesamiento del mensaje, no se solicitará a la cola, pero el consumidor volverá a intentarlo localmente.
  • Después de alcanzar el número máximo de reintentos, Spring devolverá un acuse de recibo y el mensaje se descartará.

estrategia de fracaso

Después de activar el modo de reintento, se agota el número de reintentos. Si el mensaje sigue fallando, se requiere la interfaz MessageRecoverer para manejarlo. Contiene tres implementaciones diferentes:

  • RejectAndDontRequeueRecoverer: después de que se agoten los reintentos, rechace y descarte directamente el mensaje. Este es el valor predeterminado
  • ImmediateRequeueMessageRecoverer: después de que se agotan los reintentos, se devuelve nack y el mensaje se vuelve a poner en cola
  • RepublishMessageRecoverer: después de que se agoten los reintentos, entregue el mensaje de falla al intercambio especificado

inserte la descripción de la imagen aquí

  1. Defina el intercambio y la cola para procesar mensajes fallidos en el servicio al consumidor

    @Bean
    public DirectExchange errorMessageExchange(){
          
          
        return new DirectExchange("error.direct");
    }
    @Bean
    public Queue errorQueue(){
          
          
        return new Queue("error.queue", true);
    }
    @Bean
    public Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){
          
          
        return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with("error");
    }
    
  2. Definir un RepublishMessageRecoverer, asociar colas y conmutadores

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

  • Código completo:
    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;
    
    @Configuration
    public class ErrorMessageConfig {
          
          
        @Bean
        public DirectExchange errorMessageExchange(){
          
          
            return new DirectExchange("error.direct");
        }
        @Bean
        public Queue errorQueue(){
          
          
            return new Queue("error.queue", true);
        }
        @Bean
        public Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){
          
          
            return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with("error");
        }
    
        @Bean
        public MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){
          
          
            return new RepublishMessageRecoverer(rabbitTemplate, "error.direct", "error");
        }
    }
    
  • Reiniciar la prueba:
    • Reinicie el proyecto, use la excepción simulada anterior para probar y podrá ver la información específica de la excepción en la cola de errores.
@RabbitListener(queues = "simple.queue")
    public void listenSimpleQueue(String msg) {
    
    
        System.out.println("消费者接收到simple.queue的消息:【" + msg + "】");
        //模拟异常
        System.out.println(1/0);
        log.debug("消息处理完毕!!!");
    }

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Resumir

¿Cómo garantizar la confiabilidad de los mensajes de RabbitMQ?

  • Habilite el mecanismo de confirmación del productor para asegurarse de que el mensaje del productor pueda llegar a la cola
  • Habilite la función de persistencia para garantizar que el mensaje no se pierda en la cola antes de consumirse
  • Encienda el mecanismo de confirmación del consumidor como automático, y el resorte completará el reconocimiento después de confirmar que el mensaje se procesó correctamente.
  • Habilite el mecanismo de reintento de falla del consumidor y establezca MessageRecoverer.Después de que fallan varios reintentos, el mensaje se entregará al conmutador anómalo y se transferirá al procesamiento manual.

intercambio de letra muerta

Conociendo los interruptores de letra muerta

Cuando un mensaje en una cola cumple una de las siguientes condiciones, puede convertirse en letra muerta:

  • El consumidor usa basic.reject o basic.nack para declarar el error de consumo, y el parámetro de reposición en cola del mensaje se establece en falso.
  • El mensaje es un mensaje caducado, nadie consume después del tiempo de espera
  • El mensaje de la cola que se va a entregar está lleno y no se puede entregar

Si la cola que contiene mensajes fallidos está configurada con dead-letter-exchangeatributos y se especifica un conmutador, los mensajes fallidos en la cola se entregarán a este conmutador, y este conmutador se denomina intercambio de mensajes fallidos (Dead Letter Exchange, verifique DLX).

inserte la descripción de la imagen aquí
Cuando la cola entrega el mensaje fallido al intercambio de mensajes fallidos, debe conocer dos datos:

  • nombre de cambio de letra muerta
  • La RoutingKey vinculada al intercambio de mensajes fallidos y la cola de mensajes fallidos

Solo de esta forma podemos asegurarnos de que el mensaje entregado pueda llegar al intercambio de mensajes fallidos y ser enrutado correctamente a la cola de mensajes fallidos.

inserte la descripción de la imagen aquí

Cambio de mensajes fallidos recibiendo demostración de mensajes fallidos

  • Agregue un conmutador de mensajes fallidos a simple.queue y vincule una cola al conmutador de mensajes fallidos. De esta forma, el mensaje no se descartará una vez que se convierta en mensaje fallido, sino que finalmente se entregará al intercambio de mensajes fallidos y se enrutará a la cola vinculada al intercambio de mensajes fallidos.
  • En el servicio al consumidor, defina un conjunto de conmutadores de mensajes fallidos y colas de mensajes fallidos:
// 声明普通的 simple.queue队列,并且为其指定死信交换机:dl.direct
@Bean
public Queue simpleQueue2(){
    
    
    return QueueBuilder.durable("simple.queue") // 指定队列名称,并持久化
        .deadLetterExchange("dl.direct") // 指定死信交换机
        .build();
}
// 声明死信交换机 dl.direct
@Bean
public DirectExchange dlExchange(){
    
    
    return new DirectExchange("dl.direct", true, false);
}
// 声明存储死信的队列 dl.queue
@Bean
public Queue dlQueue(){
    
    
    return new Queue("dl.queue", true);
}
// 将死信队列 与 死信交换机绑定
@Bean
public Binding dlBinding(){
    
    
    return BindingBuilder.bind(dlQueue()).to(dlExchange()).with("simple");
}

TTL (tiempo de vida)

Si un mensaje en una cola no se consume después de un tiempo de espera, se convertirá en letra muerta. Hay dos casos de tiempo de espera:

  • La cola donde se encuentra el mensaje tiene un tiempo de espera establecido
  • El mensaje en sí establece un tiempo de espera
    inserte la descripción de la imagen aquí

  1. En SpringRabbitListener del servicio del consumidor, defina un nuevo consumidor y declare el interruptor de mensajes fallidos y la cola de mensajes fallidos.
    @RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "dl.ttl.queue", durable = "true"),
        exchange = @Exchange(name = "dl.ttl.direct"),
        key = "ttl"
    ))
    public void listenDlQueue(String msg){
          
          
        log.info("接收到 dl.ttl.queue的延迟消息:{}", msg);
    }
    
  2. Declare una cola, especifique el TTL y declare el interruptor, y vincule el ttl al interruptor
    @Bean
    public Queue ttlQueue(){
          
          
        return QueueBuilder.durable("ttl.queue") // 指定队列名称,并持久化
            .ttl(10000) // 设置队列的超时时间,10秒
            .deadLetterExchange("dl.ttl.direct") // 指定死信交换机
            .build();
    }
    @Bean
    public DirectExchange ttlExchange(){
          
          
        return new DirectExchange("ttl.direct");
    }
    @Bean
    public Binding ttlBinding(){
          
          
        return BindingBuilder.bind(ttlQueue()).to(ttlExchange()).with("ttl");
    }
    
  3. Envía un mensaje, pero no especifiques un 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("发送消息成功");
    }
    
  • Registro de mensaje enviado:
    inserte la descripción de la imagen aquí
  • Ver el registro del mensaje recibido:
    inserte la descripción de la imagen aquí
  • Porque el valor TTL de la cola es de 10 segundos. Puede ver que la diferencia de tiempo entre el envío y la recepción del mensaje es exactamente de 10 segundos.
  1. Al enviar un mensaje, configure el 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("发送消息成功");
    }
    
  • Ver registro de mensajes enviados:
    15:12:16:804 DEBUG 21600 --- [           main] cn.itcast.mq.spring.SpringAmqpTest       : 发送消息成功
    
  • Recibir registro de mensajes:
    15:12:21:810  INFO 14572 --- [ntContainer#0-1] c.i.mq.listener.SpringRabbitListener     : 接收到 dl.ttl.queue的延迟消息:hello, ttl message
    
  • Cuando se establece el TTL tanto para la cola como para el mensaje, cualquiera de ellos se convertirá en letra muerta cuando caduque.

cola de retraso

  • Modo Delay Queue (Delay Queue): use TTL combinado con interruptores de mensajes fallidos para lograr el efecto de que los consumidores retrasen la recepción de mensajes después de enviarlos.
    inserte la descripción de la imagen aquí
  • Los casos de uso para las colas de retraso incluyen:
    • Retraso en el envío de SMS
    • El usuario realiza un pedido, si el usuario no paga en 15 minutos, se cancelará automáticamente
    • Programe una reunión de trabajo y notifique automáticamente a todos los participantes 20 minutos después

Debido a que hay muchas demandas de colas de retraso, RabbitMQ lanzó oficialmente el complemento DelayExchange, que admite de forma nativa los efectos de cola de retraso. Consulte la página de lista de complementos de RabbitMQ
inserte la descripción de la imagen aquí

Instale el complemento DelayExchange

  1. Dirección de descarga de la versión 3.8.9
  • Preste atención a la compatibilidad de versiones entre el complemento DelayExchange y RabbitMQ : el complemento DelayExchange3.8.9 corresponde a RabbitMQ versión 3.8.5 y superior.
  1. subir complemento
    • Debido a que se basa en la instalación de Docker, primero debe verificar el volumen de datos correspondiente al directorio del complemento RabbitMQ.
    docker volume inspect mq-plugins
    
    inserte la descripción de la imagen aquí
    • A continuación, cargue el complemento en este directorio:
      inserte la descripción de la imagen aquí
  2. instalar complemento
    • Ingrese al interior del contenedor MQ para realizar la instalación, ejecute el comando (observe el nombre del contenedor)
    docker exec -it mq1 bash
    
    • Después de ingresar al contenedor, ejecute el siguiente comando para habilitar el complemento:
    rabbitmq-plugins enable rabbitmq_delayed_message_exchange
    
    • El resultado es el siguiente:
    [root@kongyue ~]# docker exec -it mq1 bash
    root@mq1:/# rabbitmq-plugins enable rabbitmq_delayed_message_exchange
    Enabling plugins on node rabbit@mq1:
    rabbitmq_delayed_message_exchange
    The following plugins have been configured:
      rabbitmq_delayed_message_exchange
      rabbitmq_management
      rabbitmq_management_agent
      rabbitmq_prometheus
      rabbitmq_web_dispatch
    Applying plugin configuration to rabbit@mq1...
    The following plugins have been enabled:
      rabbitmq_delayed_message_exchange
    
    started 1 plugins.
    root@mq1:/# exit
    exit
    

usar complemento

El principio del complemento DelayExchange es actualizar las funciones del Exchange nativo oficial:

  • Almacene temporalmente los mensajes recibidos por DelayExchange en la memoria (el Exchange oficial no puede almacenar mensajes)

  • Temporización en DelayExchange, el mensaje se entrega a la cola después del tiempo de espera

  • Declarar un DelayExchange en la plataforma de gestión RabbitMQ
    inserte la descripción de la imagen aquí

  • El tiempo de demora del mensaje debe especificarse al enviar el mensaje
    inserte la descripción de la imagen aquí

Principio de Intercambio de Demora

DelayExchange requiere que un intercambio sea declarado como retrasado. Cuando enviamos un mensaje a delayExchange, el flujo es el siguiente:

  • recibir mensaje
  • Determinar si el mensaje tiene el atributo x-delay
  • Si hay un atributo de retraso x, significa que es un mensaje retrasado, que se conserva en el disco duro, y el valor de retraso x se lee como el tiempo de retraso.
  • Devolver el resultado de enrutamiento no encontrado al remitente del mensaje
  • Después de que expire el tiempo de retraso x, vuelva a enviar el mensaje a la cola especificada

Usar DelayExchange

  1. El uso del complemento también es muy simple: declare un interruptor, el tipo del interruptor puede ser cualquier tipo, simplemente establezca el atributo retrasado en verdadero y luego declare la cola para vincularlo.

    • Método de anotación (recomendado):
    @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);
        }
    
    • Basado en la forma de @Bean:
      inserte la descripción de la imagen aquí
  2. Enviar un mensaje

    • Al enviar un mensaje, asegúrese de llevar el atributo x-delay para especificar el tiempo de retraso:
    @Test
        public void testDelayedMsg() {
          
          
            // 创建消息
            Message message = MessageBuilder
                    .withBody("hello, delay 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("发送消息成功");
        }
    

  • Dado que el conmutador guarda la información en el disco duro y devuelve el resultado de enrutamiento no encontrado al remitente del mensaje, aparecerá el siguiente mensaje de error
    15:24:47:054 DEBUG 6124 --- [           main] cn.itcast.mq.spring.SpringAmqpTest       : 
    发送消息成功
    15:24:47:056  INFO 6124 --- [nectionFactory1] cn.itcast.mq.config.CommonConfig         : 
    消息发送失败,应答码312,原因NO_ROUTE,交换机delay.direct,路由键delay,
    消息(Body:'[B@18f05d55(byte[18])' MessageProperties [headers={
          
          spring_returned_message_correlation=aac9ebb8-f8eb-4c39-a64b-c1bf35b0c639}, 
    contentType=application/octet-stream, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, receivedDelay=10000, deliveryTag=0])
    
  1. Resuelva el problema del error

    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) -> {
          
          
                //判断是否有延迟消息
                if(message.getMessageProperties().getReceivedDelay()>0) {
          
          
                    //判断是一个延迟消息,忽视错误
                    return;
                }
                // 投递失败,记录日志
                log.info("消息发送失败,应答码{},原因{},交换机{},路由键{},消息{}",
                         replyCode, replyText, exchange, routingKey, message.toString());
                // 如果有业务需要,可以重发消息
            });
        }
    }
    

cola perezosa

problema de acumulación de mensajes

  • Cuando la velocidad a la que los productores envían mensajes supera la velocidad a la que los consumidores pueden procesar los mensajes, los mensajes en la cola se acumularán hasta que la cola almacene mensajes hasta el límite superior. Los mensajes enviados más tarde se convertirán en mensajes fallidos y pueden descartarse.Este es el problema de la acumulación de mensajes.

    inserte la descripción de la imagen aquí

  • Hay tres formas de resolver la acumulación de mensajes:

    • Agregue más consumidores y aumente la velocidad de consumo
    • Abra el grupo de subprocesos en el consumidor para acelerar el procesamiento de mensajes
    • Expanda el volumen de la cola y aumente el límite de apilamiento

cola perezosa

A partir de la versión 3.6.0 de RabbitMQ, se ha agregado el concepto de Lazy Queues, es decir, colas perezosas. Las características de la cola perezosa son las siguientes:

  • Después de recibir el mensaje, guárdelo directamente en el disco en lugar de la memoria
  • Los consumidores solo leen del disco y los cargan en la memoria cuando quieren consumir mensajes
  • Admite millones de almacenamiento de mensajes

Establecer lazy-queue basado en la línea de comando[comprender]

Para configurar una cola como cola perezosa, solo necesita especificar el atributo x-queue-mode como perezoso al declarar la cola. Una cola en ejecución se puede cambiar a una cola perezosa a través de la línea de comando:

rabbitmqctl set_policy Lazy "^lazy-queue$" '{"queue-mode":"lazy"}' --apply-to queues  

Interpretación de comandos:

  • rabbitmqctl: herramienta de línea de comandos para RabbitMQ
  • set_policy: agregar una estrategia
  • Lazy: Nombre de la póliza, que se puede personalizar
  • "^lazy-queue$": Haga coincidir el nombre de la cola con una expresión regular
  • '{"queue-mode":"lazy"}': establece el modo de cola en modo perezoso
  • --apply-to queues : El objetivo de la política son todas las colas

Declarar lazy-queue basado en @Bean

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

Declarar LazyQueue basado en @RabbitListener

@RabbitListener(queuesToDeclare = cQueue(
	name = "lazy.queue",
	arguments = @Argument(name = "x-queue-mode" ,value = "lazy")
))
public void listenLazyQueue(string msg){
    
    
	log.info("接收到lazy.queue的消息:{",msg);
}

práctica

  1. Crear una nueva clase LazyConfig
    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 LazyConfig {
          
          
        @Bean
        public Queue lazyQueue() {
          
          
            return QueueBuilder.durable("lazy.queue")
                    .lazy()
                    .build();
        }
        @Bean
        public Queue normalQueue() {
          
          
            return QueueBuilder.durable("normal.queue").build();
        }
    
    }
    
  • El modificador no se especifica explícitamente aquí, y el modificador predeterminado de Spring AMQP se usará de forma predeterminada.

    Nombre Tipo Características Tasa de mensajes en Tasa de mensajes
    (valor predeterminado de AMQP) directo D 0.00/segundo 0.00/segundo
  • El intercambio predeterminado está vinculado implícitamente a cada cola con una clave de enrutamiento igual al nombre de la cola. No se puede vincular o desvincular explícitamente del intercambio predeterminado. Tampoco se puede borrar.

@Test
    public void testLazyQueue() {
    
    
        for (int i = 0; i < 100_000; i++) {
    
    
        Message message = MessageBuilder.withBody("hello lazyQueue".getBytes(StandardCharsets.UTF_8))
                .build();

        rabbitTemplate.convertAndSend("lazy.queue",message);
        }
    }

inserte la descripción de la imagen aquí

Clúster MQ

clasificación de conglomerados

RabbitMQ está escrito en base al lenguaje Erlang, y Erlang es un lenguaje orientado a la concurrencia que, naturalmente, admite el modo de clúster. El clúster RabbitMQ tiene dos modos:

  • Clúster ordinario : es un clúster distribuido que distribuye colas a cada nodo del clúster, mejorando así la capacidad de concurrencia de todo el clúster. Los clústeres de modo normal no realizan la sincronización de datos, y cada MQ tiene su propia cola e información de datos (se sincronizará otra información de metadatos, como los conmutadores). Por ejemplo, tenemos 2 MQ: mq1 y mq2.Si su mensaje está en mq1 y está conectado a mq2, entonces mq2 irá a mq1 para extraer el mensaje y devolvérselo. Si mq1 falla, el mensaje se perderá.

  • Clúster espejo : Es un clúster maestro-esclavo. Sobre la base de los clústeres ordinarios, se agrega una función de copia de seguridad maestro-esclavo para mejorar la disponibilidad de datos del clúster. A diferencia del modo normal, la cola se sincronizará entre los nodos espejo de cada mq, para que pueda recibir el mensaje cuando se conecte a cualquier nodo espejo. Y si un nodo deja de funcionar, no se perderán datos. Sin embargo, este enfoque aumenta el consumo de ancho de banda para la sincronización de datos.

Aunque el clúster espejo admite maestro-esclavo, la sincronización maestro-esclavo no es muy consistente y puede haber riesgo de pérdida de datos en algunos casos. Por lo tanto, después de la versión 3.8 de RabbitMQ, se introdujo una nueva función: la cola de arbitraje para reemplazar el clúster espejo, y la capa inferior usa el protocolo Raft para garantizar la consistencia de los datos entre el maestro y el esclavo.

grupo normal

Estructura y características del conglomerado

Los clusters ordinarios, o clusters clásicos, tienen las siguientes características:

  • Parte de los datos se compartirán entre cada nodo del clúster, incluidos: metadatos de cambio y cola. Los mensajes en la cola no están incluidos.
  • Al acceder a un nodo en el clúster, si la cola no está en el nodo, se pasará del nodo donde se encuentran los datos al nodo actual y se devolverá
  • Si el nodo donde se encuentra la cola se cae, los mensajes en la cola se perderán
    inserte la descripción de la imagen aquí
    inserte la descripción de la imagen aquí

Implementación de clúster común

  • Plan para implementar un clúster mq de 3 nodos
nombre de la CPU puerto de consola puerto de comunicación amqp
mqNode1 8081 —> 15672 8071 —> 5672
mqNode2 8082 —> 15672 8072 —> 5672
mqNode3 8083 —> 15672 8073 —> 5672
  • Las etiquetas predeterminadas de los nodos en el clúster son: rabbit@[hostname], por lo que los nombres de los tres nodos anteriores son:
    • conejo@mqNode1
    • conejo@mqNode2
    • conejo@mqNode3

obtener galleta

  • La capa inferior de RabbitMQ depende de Erlang, y la máquina virtual de Erlang es un lenguaje orientado a la distribución que admite el modo de clúster de forma predeterminada. Cada nodo RabbitMQ en modo de clúster utiliza una cookie para determinar si se les permite comunicarse entre sí.

  • Para que dos nodos puedan comunicarse, deben tener el mismo secreto compartido, llamado cookie Erlang . Una cookie es solo una cadena de caracteres alfanuméricos de hasta 255 caracteres.

  • Cada nodo del clúster debe tener la misma cookie . También es necesario entre instancias para comunicarse entre sí.

  • Primero obtenga un valor de cookie en el contenedor mq iniciado previamente como la cookie de clúster. Ejecute el siguiente comando:

    docker exec -it mq cat /var/lib/rabbitmq/.erlang.cookie
    
  • Los valores de las cookies son los siguientes:

    FXZMCVGLBIXZCDEMMVZQ
    
  • Luego, detenga y elimine el contenedor mq actual, reconstruimos el clúster

    docker rm -f mq
    

Preparar la configuración del clúster

echo FXZMCVGLBIXZCDEMMVZQ> .erlang.cookie
  • Cree un nuevo archivo de configuración rabbitmq.conf en el directorio /tmp/rabbitMqCluster:
    cd /tmp/rabbitMqCluster
    touch rabbitmq.conf
    
  • El contenido del archivo es el siguiente:
    loopback_users.guest = false
    listeners.tcp.default = 5672
    cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
    cluster_formation.classic_config.nodes.1 = rabbit@mqNode1
    cluster_formation.classic_config.nodes.2 = rabbit@mqNode2
    cluster_formation.classic_config.nodes.3 = rabbit@mqNode3
    
  • Crear otro archivo para registrar cookies
    cd /tmp/rabbitMqCluster
    # 创建cookie文件
    touch .erlang.cookie
    # 写入cookie 请使用自己生成的cookie
    echo "FXZMCVGLBIXZCDEMMVZQ" > .erlang.cookie
    # 修改cookie文件的权限
    chmod 600 .erlang.cookie
    
  • Prepare tres directorios, mqNode1, mqNode2, mqNode3:
    cd /tmp/rabbitMqCluster
    # 创建目录
    mkdir mqNode1 mqNode2 mqNode3
    
  • Luego copie rabbitmq.conf
    [root@kongyue rabbitMqCluster]# mkdir mqNode1 mqNode2 mqNode3
    [root@kongyue rabbitMqCluster]# cp .erlang.cookie mqNode1
    [root@kongyue rabbitMqCluster]# cp .erlang.cookie mqNode2
    [root@kongyue rabbitMqCluster]# cp .erlang.cookie mqNode3
    [root@kongyue rabbitMqCluster]# cp rabbitmq.conf mqNode1
    [root@kongyue rabbitMqCluster]# cp rabbitmq.conf mqNode2
    [root@kongyue rabbitMqCluster]# cp rabbitmq.conf mqNode3
    

Inicie el clúster

  • Crear una red:
    docker network create mq-net
    
  • ejecutar comando
    docker run -d --net mq-net \
    -v ${
          
          PWD}/mqNode1/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
    -v ${
          
          PWD}/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
    -v mq-plugins:/plugins \
    -e RABBITMQ_DEFAULT_USER=itcast \
    -e RABBITMQ_DEFAULT_PASS=123321 \
    --name mqNode1 \
    --hostname mqNode1 \
    -p 8071:5672 \
    -p 8081:15672 \
    rabbitmq:3.8-management
    
    docker run -d --net mq-net \
    -v ${
          
          PWD}/mqNode2/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
    -v ${
          
          PWD}/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
    -v mq-plugins:/plugins \
    -e RABBITMQ_DEFAULT_USER=itcast \
    -e RABBITMQ_DEFAULT_PASS=123321 \
    --name mqNode2 \
    --hostname mqNode2 \
    -p 8072:5672 \
    -p 8082:15672 \
    rabbitmq:3.8-management
    
    docker run -d --net mq-net \
    -v ${
          
          PWD}/mqNode3/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
    -v ${
          
          PWD}/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
    -v mq-plugins:/plugins \
    -e RABBITMQ_DEFAULT_USER=itcast \
    -e RABBITMQ_DEFAULT_PASS=123321 \
    --name mqNode3 \
    --hostname mqNode3 \
    -p 8073:5672 \
    -p 8083:15672 \
    rabbitmq:3.8-management
    

prueba

  • Inicie sesión en cualquier nodo y vea el nodo
    inserte la descripción de la imagen aquí
  • Agregue una cola al nodo mq1:
    inserte la descripción de la imagen aquí
  • También se puede ver en las consolas mq2 y mq3:
    inserte la descripción de la imagen aquí

Prueba de intercambio de datos

  • Usa la consola para enviar un mensaje a esta cola
    inserte la descripción de la imagen aquí
    inserte la descripción de la imagen aquí

modo espejo

Estructura y características del conglomerado

Grupo de espejos: la esencia es el modo maestro-esclavo, con las siguientes características:

  • Los conmutadores, las colas y los mensajes en las colas se respaldarán de forma síncrona entre los nodos espejo de cada mq.
  • El nodo que crea la cola se denomina nodo principal de la cola y los otros nodos en los que se realiza la copia de seguridad se denominan nodos espejo de la cola.
  • El nodo maestro de una cola puede ser el nodo espejo de otra cola
  • Todas las operaciones son completadas por el nodo maestro y luego sincronizadas con el nodo espejo
  • Después de que el maestro deje de funcionar, el nodo espejo será reemplazado por el nuevo maestro

inserte la descripción de la imagen aquí
Resumir:

  • La estructura de la cola espejo es un maestro y múltiples esclavos (el esclavo es una imagen espejo)
  • Todas las operaciones son completadas por el nodo maestro y luego sincronizadas con el nodo espejo
  • Después de que el maestro se apague, el nodo espejo lo reemplazará como el nuevo maestro (si el maestro se cae antes de que se complete la sincronización maestro-esclavo, puede ocurrir una pérdida de datos)
  • No tiene función de equilibrio de carga, porque el nodo maestro completará todas las operaciones (pero diferentes colas, el nodo maestro puede ser diferente, puede usar esto para mejorar el rendimiento)

Configuración del modo espejo

  • Hay 3 modos para la configuración del modo espejo:
modo ha ha-params Efecto
modo exacto exactamente El número de copias del recuento de la cola. El número de réplicas de cola (suma de servidores principal y reflejado) en el clúster. Si el recuento es 1, significa una sola copia: el nodo maestro de la cola. Un valor de conteo de 2 significa 2 copias: 1 maestro de cola y 1 espejo de cola. En otras palabras: cuenta = número de espejos + 1. Si hay menos de nodos en el clúster, la cola se reflejará en todos los nodos. Si hay un total de clúster mayor que count+1 y el nodo que contiene el espejo falla, se creará un nuevo espejo en otro nodo.
todo (ninguno) Las colas se reflejan en todos los nodos del clúster. La cola se reflejará en cualquier nodo recién unido. La duplicación de todos los nodos ejercerá una presión adicional sobre todos los nodos del clúster, incluida la E/S de red, la E/S de disco y el uso de espacio en disco. Se recomienda usar exactamente y establecer el número de réplicas en (N / 2 +1).
nodos nombres de nodos Especifique en qué nodos se crea la cola. Si no existe ninguno de los nodos especificados, se producirá una excepción. Si el nodo especificado existe en el clúster pero no está disponible temporalmente, se creará un nodo en el nodo al que está conectado el cliente actual.

  • Tome el comando rabbitmqctl como ejemplo para explicar la sintaxis de configuración.
  1. modo exacto
    rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
    
  • rabbitmqctl set_policy: Redacción fija
  • ha-two: nombre de la política, personalizado
  • "^two\.": haga coincidir la expresión regular de la cola, la cola que cumpla con las reglas de nomenclatura tendrá efecto, aquí hay cualquier two.nombre de cola que comience con
  • '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}': Contenido de la política
    • "ha-mode":"exactly": modo de estrategia, aquí está exactamente el modo, especifique el número de copias
    • "ha-params":2: Parámetro de política, aquí hay 2, es decir, el número de réplicas es 2, 1 maestro y 1 espejo
    • "ha-sync-mode":"automatic": estrategia de sincronización, el valor predeterminado es manual, es decir, los nodos espejo recién agregados no sincronizarán los mensajes antiguos. Si se establece en automático, el nodo espejo recién agregado sincronizará todos los mensajes en el nodo maestro, lo que generará una sobrecarga de red adicional.
  1. todos los modos
    rabbitmqctl set_policy ha-all "^all\." '{"ha-mode":"all"}'
    
  • ha-all: nombre de la política, personalizado
  • "^all\.": coincide con todos all.los nombres de cola que comienzan con
  • '{"ha-mode":"all"}': contenido de la política
    • "ha-mode":"all": Modo de estrategia, aquí está todo el modo, es decir, todos los nodos se llamarán nodos espejo
  1. modo de nodos
    rabbitmqctl set_policy ha-nodes "^nodes\." '{"ha-mode":"nodes","ha-params":["rabbit@nodeA", "rabbit@nodeB"]}'
    
  • rabbitmqctl set_policy: Redacción fija
  • ha-nodes: nombre de la política, personalizado
  • "^nodes\.": haga coincidir la expresión regular de la cola, la cola que cumpla con las reglas de nomenclatura tendrá efecto, aquí hay cualquier nodes.nombre de cola que comience con
  • '{"ha-mode":"nodes","ha-params":["rabbit@nodeA", "rabbit@nodeB"]}': Contenido de la política
    • "ha-mode":"nodes": Modo de estrategia, aquí está el modo de nodos.
    • "ha-params":["rabbit@mq1", "rabbit@mq2"]: Parámetro de política, aquí especifique el nombre del nodo donde se encuentra la copia

Probar exactamente el modo

  • Utilice la duplicación en modo exacto, ya que la cantidad de nodos del clúster es 3, por lo que la cantidad de duplicación se establece en 2, ejecute el siguiente comando:
    docker exec -it mqNode1 rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
    
[root@kongyue rabbitMqCluster]# docker exec -it mqNode1 rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
Setting policy "ha-two" for pattern "^two\." to "{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}" with priority "0" for vhost "/" ...

  • Crear una nueva cola:
    inserte la descripción de la imagen aquí
  • Verifique la cola en cualquier consola mq:
    inserte la descripción de la imagen aquí

Compartición de datos de prueba

-  给two.queue发送一条消息:

inserte la descripción de la imagen aquí

  • Luego verifique el mensaje en cualquier consola de mq1, mq2, mq3:
    inserte la descripción de la imagen aquí

Probar alta disponibilidad

  • Deje que el nodo maestro mq1 de two.queue baje:
    docker stop mqNode1
    
  • Ver el estado del clúster:
    inserte la descripción de la imagen aquí
  • Ver el estado de la cola:
    inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

Cola de arbitraje

características del conglomerado

Cola de arbitraje: La cola de arbitraje es una nueva función disponible después de la versión 3.8.Se utiliza para reemplazar la cola espejo y tiene las siguientes características:

  • Al igual que la cola espejo, es un modo maestro-esclavo y admite la sincronización de datos maestro-esclavo
  • Muy fácil de usar, sin configuraciones complicadas
  • Sincronización maestro-esclavo basada en el protocolo Raft, consistencia fuerte

Implementación: agregar cola de quórum

  • Nota: Ingrese cada contenedor de rabbitMQ para abrir el complemento DelayExchange de retraso
  • Para agregar una cola en cualquier consola, asegúrese de seleccionar el tipo de cola como Tipo de quórum.
    inserte la descripción de la imagen aquí
  • Ver colas en cualquier consola:
    inserte la descripción de la imagen aquí
    • +2 palabra para cola de quórum. Significa que esta cola tiene 2 nodos espejo.
  • Porque el número de espejo predeterminado de la cola de quórum es 5. Si el clúster tiene 7 nodos, entonces la cantidad de espejos debe ser 5; ahora el clúster tiene solo 3 nodos, por lo que la cantidad de espejos es 3.
  • Ver los detalles de la cola en cada nodo
    inserte la descripción de la imagen aquí

Código Java para crear cola de quórum

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

SpringAMQP se conecta al clúster MQ

spring:
  rabbitmq:
    addresses: 192.168.188.112:8071, 192.168.188.112:8072, 192.168.188.112:8073
    username: itcast
    password: 123321
    virtual-host: /
  • Reinicie el proyecto y verifique la cola:
    inserte la descripción de la imagen aquí

Expansión del clúster

Únete al clúster

  1. Inicie un nuevo contenedor MQ:
    docker run -d --net mq-net \
    -v ${PWD}/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
    -e RABBITMQ_DEFAULT_USER=itcast \
    -e RABBITMQ_DEFAULT_PASS=123321 \
    --name mqNode4 \
    --hostname mqNode4 \
    -p 8074:15672 \
    -p 8084:15672 \
    rabbitmq:3.8-management
    
  2. Ingrese a la consola del contenedor:
    docker exec -it mqNode4 bash
    
  3. detener el proceso mq
    rabbitmqctl stop_app
    
  4. Restablecer datos en RabbitMQ:
    rabbitmqctl reset
    
  5. Únase a mqNode1:
    rabbitmqctl join_cluster rabbit@mqNode1
    
  6. Inicie el proceso mq de nuevo
    rabbitmqctl start_app
    
[root@kongyue rabbitMqCluster]# docker exec -it mqNode4 bash
root@mqNode4:/# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@mqNode4 ...
root@mqNode4:/# rabbitmqctl reset
Resetting node rabbit@mqNode4 ...
root@mqNode4:/# rabbitmqctl join_cluster rabbit@mqNode1
Clustering node rabbit@mqNode4 with rabbit@mqNode1
root@mqNode4:/# rabbitmqctl start_app
Starting node rabbit@mqNode4 ...
  • resultado:
    inserte la descripción de la imagen aquí

Agregar copia de cola de quórum

  • Primero verifique la copia actual de la cola quorum.queue, ingrese el contenedor mqNode1 y ejecute el comando:
[root@kongyue rabbitMqCluster]# docker exec -it mqNode1 bash
root@mqNode1:/# rabbitmq-queues quorum_status "quorum.queue"
Status of quorum queue quorum.queue on node rabbit@mqNode1 ...
┌────────────────┬────────────┬───────────┬──────────────┬────────────────┬──────┬─────────────────┐
│ Node Name      │ Raft State │ Log Index │ Commit Index │ Snapshot Index │ Term │ Machine Version │
├────────────────┼────────────┼───────────┼──────────────┼────────────────┼──────┼─────────────────┤
│ rabbit@mqNode3 │ follower   │ 22            │ undefined      │ 11
├────────────────┼────────────┼───────────┼──────────────┼────────────────┼──────┼─────────────────┤
│ rabbit@mqNode2 │ leader     │ 22            │ undefined      │ 11
├────────────────┼────────────┼───────────┼──────────────┼────────────────┼──────┼─────────────────┤
│ rabbit@mqNode1 │ follower   │ 22            │ undefined      │ 11
└────────────────┴────────────┴───────────┴──────────────┴────────────────┴──────┴─────────────────┘

  • Únase a mqNode4:
root@mqNode1:/# rabbitmq-queues add_member "quorum.queue" "rabbit@mqNode4"
Adding a replica for queue quorum.queue on node rabbit@mqNode4...
root@mqNode1:/# rabbitmq-queues quorum_status "quorum.queue"
Status of quorum queue quorum.queue on node rabbit@mqNode1 ...
┌────────────────┬────────────┬───────────┬──────────────┬────────────────┬──────┬─────────────────┐
│ Node Name      │ Raft State │ Log Index │ Commit Index │ Snapshot Index │ Term │ Machine Version │
├────────────────┼────────────┼───────────┼──────────────┼────────────────┼──────┼─────────────────┤
│ rabbit@mqNode4 │ follower   │ 33            │ undefined      │ 11
├────────────────┼────────────┼───────────┼──────────────┼────────────────┼──────┼─────────────────┤
│ rabbit@mqNode3 │ follower   │ 33            │ undefined      │ 11
├────────────────┼────────────┼───────────┼──────────────┼────────────────┼──────┼─────────────────┤
│ rabbit@mqNode2 │ leader     │ 33            │ undefined      │ 11
├────────────────┼────────────┼───────────┼──────────────┼────────────────┼──────┼─────────────────┤
│ rabbit@mqNode1 │ follower   │ 33            │ undefined      │ 11
└────────────────┴────────────┴───────────┴──────────────┴────────────────┴──────┴─────────────────┘

  • Mirando la consola, el número de espejos de quorum.queue también ha cambiado de +2 a +3:
    inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/yang2330648064/article/details/130211585
Recomendado
Clasificación