RabbitMQ高级之消息可靠性投递

什么是可靠性投递?

生产者:作为消息发送方希望杜绝任何消息丢失或者投递失败场景。

RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。

分别是:

  • confirm 确认模式
  • return 退回模式

消息投递路线

如下所示是生产者到消费者的模型:

RabbitMQ的整个消息投递的路径

Producer--->RabbitMQ Broker(Server)--->Exchange--->Queue--->Consumer

  • 消息从 producerexchange 则会返回一个 confirmCallback
  • 消息从 exchange 到 queue 投递失败则会返回一个 returnCallback

我们将利用这两个 callback 控制消息的可靠性投递。

确认模式

消息从 producerexchange 则会返回一个 confirmCallback ,不论消息是否成功到达exchange ,回调都会执行,只不过返回的bool类型的值是true or false的区别。

代码实现很简单,两步即可

步骤

(1)在XML配置文件中设置 ConnectionFactory 开启 publisher-confirms="true"

(2)在 rabbitTemplate 定义 ConfirmCallBack() 回调函数

代码实现 

spring-rabbitmq-producer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xmlns:rabb="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!-- 1.加载配置文件-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- 2.定义rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"
                               
                               publisher-confirms="true"/>
    <!-- publisher-confirms="true" 确认模式开启!!! -->

    <!-- 3.定义管理交换机、队列-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <!-- 消息的可靠性投递 -->
    <rabbit:queue id="test_queue_confirm" name="test_queue_confirm"></rabbit:queue>
    <rabbit:direct-exchange name="test_exchange_confirm">
        <rabbit:bindings>
            <rabbit:binding queue="test_queue_confirm" key="confirm"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!-- 定义rabbitTemplate对象操作可以在代码中方便发送消息-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>

</beans>

ProducerTest

package com.Harmony;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {
    /**
     *   确认模式
     *   开启:
     *   1. ConnectionFactory 中开启 publisher-confirms="true"
     *   2. 在 rabbitTemplate 定义 ConfirmCallBack() 回调函数
     */
    @Autowired
    private RabbitTemplate rabbitTemplate;
    // 2. 定义回调

    // 一、确认模式
    @Test
    public void testConfirm() throws InterruptedException {
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData: 配置信息,在convertAndSend()中的重载方法有该参数
             * @param ack: 代表exchange交换机是否收到了信息,true为成功,false为失败
             * @param cause: 失败原因; 如果成功为null
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println(ack);
                System.out.println("confirm方法被执行了......");
                if (ack) {
                    System.out.println("接收成功:" + cause);
                } else {
                    System.out.println("接收失败:" + cause);
                    // 以后可能会做一些处理
                }
            }
        });
        // 3. 发送消息
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message confirm......");
        // 由于在测试种,执行结束线程直接没有了!
        Thread.sleep(2000);
    }
   
}

退回模式

当消息从exchange路由到 queue失败后,如果设置了rabbitTemplate.setMandatory(true)参数,则会将消息退 回给producer。并执行回调函数returnedMessage。

步骤

(1)开启回退模式 publisher-returns="true"

(2)设置ReturnCallback

(3)设置Exchange处理消息的模式:

        如果消息没有路由到Queue,则会丢弃消息(默认)

        如果消息没有路由到Queue,返回给消息发送方

代码实现

spring-rabbitmq-producer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xmlns:rabb="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!-- 1.加载配置文件-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- 2.定义rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"

                               publisher-returns="true" />
    <!-- publisher-returns="true" 回退模式开启!!! -->

    <!-- 3.定义管理交换机、队列-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <!-- 消息的可靠性投递 -->
    <rabbit:queue id="test_queue_confirm" name="test_queue_confirm"></rabbit:queue>
    <rabbit:direct-exchange name="test_exchange_confirm">
        <rabbit:bindings>
            <rabbit:binding queue="test_queue_confirm" key="confirm"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!-- 定义rabbitTemplate对象操作可以在代码中方便发送消息-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>

</beans>

ProducerTest

package com.Harmony;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testReturn() throws InterruptedException {
        // 设置交换机处理失败消息的模式
        // 生产者可以拿到之前发送失败的消息
        rabbitTemplate.setMandatory(true);

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             *
             * @param message: 消息对象
             * @param replyCode: 错误码
             * @param replyText: 错误信息
             * @param exchange: 交换机
             * @param routingKey: 路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("return 执行了...");
                System.out.println(message);
                System.out.println(replyCode);
                System.out.println(replyText);
                System.out.println(exchange);
                System.out.println(routingKey);
            }
        });
        // 3. 发送消息
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message confirm......");
        Thread.sleep(2000);
    }

}

总述

➢ 设置ConnectionFactory的publisher-confirms="true" 开启确认模式。

➢ 使用rabbitTemplate.setConfirmCallback设置回调函数。当消息发送到exchange后回 调confirm方法。在方法中判断ack,如果为true,则发送成功,如果为false,则发 送失败,需要处理。

➢ 设置ConnectionFactory的publisher-returns="true" 开启退回模式。

➢ 使用rabbitTemplate.setReturnCallback设置退回函数,当消息从exchange路由到 queue失败后,如果设置了rabbitTemplate.setMandatory(true)参数,则会将消息退 回给producer。并执行回调函数returnedMessage。

➢ 在RabbitMQ中也提供了事务机制,但是性能较差,此处不做讲解。 使用channel下列方法,完成事务控制:

  • txSelect(), 用于将当前channel设置成transaction模式
  • txCommit(),用于提交事务
  • txRollback(),用于回滚事务

猜你喜欢

转载自blog.csdn.net/weixin_43715214/article/details/125175420