RabbitMQ 高级特性 - 消息的可靠性投递
- 使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。
- RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。
- confirm 确认模式
- return 退回模式
- rabbitmq 整个消息投递的路径为:
producer--->rabbitmq broker--->exchange--->queue--->consumer
生产者--->rabbitmq中间件--->交换机--->队列--->消费者
- confirm 模式: 消息从 producer 到 exchange 则会返回一个 confirmCallback
- return 退回模式: 消息从 exchange–>queue 投递失败则会返回一个 returnCallback
- confirm 确认模式
- 步骤
-
创建工程
-
pom.xml导包
<dependencies> <!--spring 上下文--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.7.RELEASE</version> </dependency> <!-- spring 整合rabbit--> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>2.1.8.RELEASE</version> </dependency> <!-- 单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.7.RELEASE</version> </dependency> </dependencies> <build> <plugins> <!-- 编译插件包--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
-
resources目录下创建
rabbitmq.properties
文件rabbitmq.host=192.168.20.146 rabbitmq.port=5672 rabbitmq.username=test rabbitmq.password=test rabbitmq.virtual-host=/test
-
resources目录下创建
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" 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"> <!--加载配置文件--> <context:property-placeholder location="classpath:/rabbitmq.properties"/> <!-- 定义rabbitmq connectionFactory --> <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}" virtual-host="${rabbitmq.virtual-host}"/> <!--定义管理交换机、队列--> <rabbit:admin connection-factory="connectionFactory"/> <!--定义rabbitTemplate对象操作可以在代码中方便发送消息--> <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/> </beans>
-
在
spring-rabbitmq-producer.xml
文件创建队列和交换机<!-- 1. 创建队列 --> <rabbit:queue id="test_queue_confirm" name="test_queue_confirm"/> <!-- 2. 创建交换机 --> <rabbitmq:direct-exchange name="test_exchange_confirm"> <rabbit:bindings> <rabbit:binding queue="test_queue_confirm" key="confirm"/> </rabbit:bindings> </rabbitmq:direct-exchange>
-
创建测试类
package com.yang.test; import org.junit.runner.RunWith; 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; }
-
编写确认模式步骤
-
ConnectionFactory中开启配置(在配置文件里面)
-
在rabbitmqTemplate定义ConfirmCollBack回调函数
@Test public void testConfirm() { //2. 定义回调函数 rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { System.out.println("confirm方法被执行了...."); } }); }
-
发送消息测试一下
@Test public void testConfirm() { //2. 定义回调函数 rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { System.out.println("confirm方法被执行了...."); } }); //3. 发送消息 rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message confirm ..."); }
-
尝试执行一下(
此时回调函数会执行,并且消息也在队列里面了
)
-
public void confirm(CorrelationData correlationData, boolean ack, String cause)
参数详解/** * * @param correlationData 相关配置信息 * @param ack 代表exchange交换机是否成功收到消息.true 收到, false 没收到 * @param cause 失败的原因 */
-
尝试执行(
成功的和失败(故意把交换机名称写错test1_exchange_confirm)
)@Test public void testConfirm() { //2. 定义回调函数 rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { /** * * @param correlationData 相关配置信息 * @param ack 代表exchange交换机是否成功收到消息.true 收到, false 没收到 * @param cause 失败的原因 */ @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (ack) { System.out.println("接受消息成功"); }else { System.out.println("接受消息失败"+cause); } System.out.println("confirm方法被执行了...."); } }); //3. 发送消息 rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message confirm ..."); }
-
我们也可以直接通过lambda表达式来写回调函数
rabbitTemplate.setConfirmCallback((correlationData,ack,cause)->{ });
-
-
- 步骤
- return 退回模式
- 步骤(
1-6都是一样的
)- 编写回退模式步骤
-
ConnectionFactory中开启配置(在配置文件里面)
-
设置对应的ReturnCallBack(
直接用lambda表达式
)此时默认是丢弃,下面这句话是不会执行的!!!
使用了不存在的routingKeyconfirm1
@Test public void testReturn() { //2. 设置对应的ReturnCallBack rabbitTemplate.setReturnCallback((message, replyCode, replyText ,exchange, routingKey)->{ System.out.println("return 执行了...."); }); //3. 发送消息 rabbitTemplate.convertAndSend("test1_exchange_confirm","confirm","message confirm ..."); }
-
设置Exchange处理消息的模式
rabbitTemplate.setMandatory(true)
:-
如果消息没有路由到Queue,则丢弃消息(默认)
-
如果消息没有路由到Queue,返回给消息发送方ReturnCallBack
@Test public void testReturn() { // 设置交换机处理失败消息的模式 rabbitTemplate.setMandatory(true); //2. 设置对应的ReturnCallBack rabbitTemplate.setReturnCallback((message, replyCode, replyText ,exchange, routingKey)->{ System.out.println("return 执行了...."); }); //3. 发送消息 rabbitTemplate.convertAndSend("test_exchange_confirm","confirm1","message confirm ..."); }
-
运行结果
-
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey)
参数详解/** * * @param message 消息对象 * @param replyCode 失败的错误码 * @param replyText 错误信息 * @param exchange 交换机 * @param routingKey 路由键 */
-
-
- 编写回退模式步骤
- 步骤(
- 消息的可靠投递小结
-
设置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(),用于回滚事务
- 使用channel下列方法,完成事务控制:
-