开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情
在使用RabbitMQ的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败的场景,RabbitMQ为我们提供了两种方式用来控制消息的投递可靠性模式
RabbitMQ整个消息投递的流程路径为
producer(生产者)->rabbitmq broker(中间件)->exchange(交互机)->queue(队列)->consumer(消费者)
1 confirm 确认模式
消息从producer到exchange则返回一个confirmCallBack(回调函数),不管消息是否成功发送,这个回调函数都会执行,只不过返回值不同(true或者false)
pom
<dependencies>
<!--spring上下文-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<!--spring整合amqp-->
<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>
配置文件
#ip 172.16.98.133
rabbitmq.host=localhost
#端口
rabbitmq.port=5672
#用户名
rabbitmq.username=weiyihe
#密码
rabbitmq.password=weiyihe
#虚拟机
rabbitmq.virtual-host= /itcast_wyh
<? 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:application.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}"
publisher-confirms="true"
/>
<!--定义管理交换机,队列-->
<rabbit:admin connection-factory="connectionFactory" />
<!--定义rabbitTemplate对象操作可以在代码中调用api发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory" />
<!--消息的可靠性投递(生产端)-->
<!--队列-->
<rabbit:queue id="test_queue_confirm" name="test_queue_confirm" ></rabbit:queue>
<!--交换机 广播-->
<rabbit:direct-exchange name="test_exchange_confirm" >
<!--绑定queue-->
<rabbit:bindings>
<rabbit:binding queue="test_queue_confirm" key="confirm" ></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
</beans>
测试代码
package com.producer.test;
import org.junit.Test;
import org.junit.runner.RunWith;
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;
/**
* @program: SpringBoot_RabbitMQ_Advanced
* @description:测试确认模式消息是否发送成功
* @author: 魏一鹤
* @createDate: 2022-04-04 23:10
**/
//spring配置文件
@RunWith(SpringJUnit4ClassRunner.class)
//加载文件的路径
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml" )
public class ProducerTest {
//注入 RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 确认模式:
* 步骤
* 1确认模式的开启:在connectionFactory中开启,默认是false不开启的 publisher-confirms="true"
* 2回调函数的编写:在RabbitTemplate模板工具类定义ConfirmCallBack(回调函数).当消息发送出去的时候回调函数会自动执行,返回true(成功)或者false(失败)
**/
@Test
public void testConfirm() {
//定义确认模式的回调函数
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
//匿名内部类
/**
* confirm有三个参数 下面一一说明
* CorrelationData correlationData 相关的配置信息
* boolean ack exchange交换机是否成功收到了消息 true成功false失败
* String cause 失败原因 ack=false
**/
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println( "确认模式的回调函数被执行了!" ); //确认模式的回调函数被执行了!
System.out.println( "消息是否发送成功?" +ack);
if(ack){
//交换机接收成功生产者发送的消息
System.out.println( "接收成功消息!原因是:" +cause);
}else{
//交换机接收没有成功生产者发送的消息
System.out.println( "接收失败消息!原因是:" +cause);
//将来会做处理,就算消息发送失败也会重新去发送消息,保证消息第二次发送成功
}
}
});
//发送消息
rabbitTemplate.convertAndSend( "test_exchange_confirm" , "confirm" , "message confirm..." );
}
}
1确认模式的开启:在connectionFactory中开启,默认是false不开启的 publisher-confirms="true"
2回调函数的编写:在RabbitTemplate模板工具类定义ConfirmCallBack(回调函数).当消息发送出去的时候回调函数会自动执行,返回true(成功)或者false(失败)
package com.producer.test;
import org.junit.Test;
import org.junit.runner.RunWith;
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;
/**
* @program: SpringBoot_RabbitMQ_Advanced
* @description:
* @author: 魏一鹤
* @createDate: 2022-04-04 23:10
**/
//spring配置文件
@RunWith(SpringJUnit4ClassRunner.class)
//加载文件的路径
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml" )
public class ProducerTest {
//注入 RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 确认模式:
* 步骤
* 1确认模式的开启:在connectionFactory中开启,默认是false不开启的 publisher-confirms="true"
* 2回调函数的编写:在RabbitTemplate模板工具类定义ConfirmCallBack(回调函数).当消息发送出去的时候回调函数会自动执行,返回true(成功)或者false(失败)
**/
@Test
public void testConfirm() {
//定义确认模式的回调函数
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
//匿名内部类
/**
* confirm有三个参数 下面一一说明
* CorrelationData correlationData 相关的配置信息
* boolean ack exchange交换机是否成功收到了消息 true成功false失败
* String cause 失败原因 ack=false
**/
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println( "确认模式的回调函数被执行了!" ); //确认模式的回调函数被执行了!
System.out.println( "消息是否发送成功?" +ack);
if(ack){
//交换机接收成功生产者发送的消息
System.out.println( "接收成功消息!原因是:" +cause);
}else{
//交换机接收没有成功生产者发送的消息
System.out.println( "接收失败消息!原因是:" +cause);
//将来会做处理,就算消息发送失败也会重新去发送消息
}
}
});
//发送消息
rabbitTemplate.convertAndSend( "test_exchange_confirm" , "confirm" , "message confirm..." );
}
}
测试成功发送消息
确认模式的回调函数被执行了!
消息是否发送成功?true
接收成功消息!原因是:null
测试失败发送消息
错误修改交换机的名称,找不到交换机名称就会报错,验证发送消息失败
确认模式的回调函数被执行了!
消息是否发送成功?false
接收失败消息!原因是:channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'test_exchange_confirm1111' in vhost '/itcast_wyh', class-id=60, method-id=40)
2 return退回模式
消息从exchange->queue投递失败则会返回一个returnCallBack(回调函数)
我们可以利用以上两个callBack(回调函数)控制消息的可靠性传递(是否发送消息成功)
1回退模式的开启:在connectionFactory中开启,默认是false不开启的 publisher-returns="true"
2回调函数的编写:在RabbitTemplate模板工具类定义ConfirmCallBack(回调函数).当消息发送出去的时候回调函数会自动执行,返回true(成功)或者false(失败)
package com.producer.test;
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;
/**
* @prog ram: SpringBoot_RabbitMQ_Advanced
* @description: 测试确认模式消息是否发送成功
* @author: 魏一鹤
* @createDate: 2022-04-04 23:10
**/
//spring配置文件
@RunWith(SpringJUnit4ClassRunner.class)
//加载文件的路径
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml" )
public class ProducerTest {
//注入 RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 回退模式:当消息发送给Exchange交换机后,交换机路由到queue失败时才会执行ReturnCallBack
* 步骤
* 1回退模式的开启:在connectionFactory中开启,默认是false不开启的 publisher-returns="true"
* 2回调函数的编写:在RabbitTemplate模板工具类定义ConfirmCallBack(回调函数).当消息发送出去的时候回调函数会自动执行,返回true(成功)或者false(失败)
* 3设置Exchange处理消息的模式 它有两种模式
* 3.1 第一种模式:如果消息没有路由到queue队列,则会丢弃消息(默认的方式)
* 3.2 第二种模式:如果消息没有路由到queue队列,则返回给消息的发送方ReturnCallBack
**/
@Test
public void testReturn() {
//由于处理消息的模式默认是如果消息没有路由到queue队列,则会丢弃消息
//所以需要设置交换机处理消息的模式,交换机会把消息返回给对应的生产者,生产者通过监听就能拿到消息
rabbitTemplate.setMandatory(true);
//编写returnCallBack回调函数
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback(){
@Override
public void returnedMessage(Message message, int i, String s, String s1, String s2) {
System.out.println( "返回模式的回调函数被执行了!" );
}
});
//发送消息
rabbitTemplate.convertAndSend( "test_exchange_confirm" , "confirm111" , "message confirm..." );
}
}
3设置Exchange处理消息的模式 它有两种模式
第一种模式:如果消息没有路由到queue队列,则会丢弃消息(默认的方式)
测试发送消息失败第一种模式,丢弃消息
第二种模式:如果消息没有路由到queue队列,则返回给消息的发送方ReturnCallBack
<? 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:application.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}"
publisher-confirms="true"
publisher-returns="true"
/>
<!--定义管理交换机,队列-->
<rabbit:admin connection-factory="connectionFactory" />
<!--定义rabbitTemplate对象操作可以在代码中调用api发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory" />
<!--消息的可靠性投递(生产端)-->
<!--队列-->
<rabbit:queue id="test_queue_confirm" name="test_queue_confirm" ></rabbit:queue>
<!--交换机 广播-->
<rabbit:direct-exchange name="test_exchange_confirm" >
<!--绑定queue-->
<rabbit:bindings>
<rabbit:binding queue="test_queue_confirm" key="confirm" ></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
</beans>
//由于处理消息的模式默认是如果消息没有路由到queue队列,则会丢弃消息
//所以需要设置交换机处理消息的模式,交换机会把消息返回给对应的生产者,生产者通过监听就能拿到消息
rabbitTemplate.setMandatory(true);
测试发送消息失败第二种模式,调用回调函数告知信息
package com.producer.test;
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;
/**
* @prog ram: SpringBoot_RabbitMQ_Advanced
* @description: 测试确认模式消息是否发送成功
* @author: 魏一鹤
* @createDate: 2022-04-04 23:10
**/
//spring配置文件
@RunWith(SpringJUnit4ClassRunner.class)
//加载文件的路径
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml" )
public class ProducerTest {
//注入 RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 回退模式:当消息发送给Exchange交换机后,交换机路由到queue失败时才会执行ReturnCallBack
* 步骤
* 1回退模式的开启:在connectionFactory中开启,默认是false不开启的 publisher-returns="true"
* 2回调函数的编写:在RabbitTemplate模板工具类定义ConfirmCallBack(回调函数).当消息发送出去的时候回调函数会自动执行,返回true(成功)或者false(失败)
* 3设置Exchange处理消息的模式 它有两种模式
* 3.1 第一种模式:如果消息没有路由到queue队列,则会丢弃消息(默认的方式)
* 3.2 第二种模式:如果消息没有路由到queue队列,则返回给消息的发送方ReturnCallBack
**/
@Test
public void testReturn() {
//由于处理消息的模式默认是如果消息没有路由到queue队列,则会丢弃消息
//所以需要设置交换机处理消息的模式,交换机会把消息返回给对应的生产者,生产者通过监听就能拿到消息
rabbitTemplate.setMandatory(true);
//编写returnCallBack回调函数
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback(){
@Override
//匿名内部类
/**
* returnedMessage有五个参数 下面一一说明
* Message message 消息对象
* int replyCode 返回编码 错误码
* String replyText 错误信息
* String exchange 交换机
* String routingKey 路由键
**/
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println( "返回模式的回调函数被执行了!" );
System.out.println( "消息对象:" +message);
System.out.println( "返回编码(错误码):" +replyCode);
System.out.println( "错误信息:" +replyText);
System.out.println( "交换机:" +exchange);
System.out.println( "路由键:" +routingKey);
//将来会做处理 把信息重新路由
//-----------------------------------------打印信息
//返回模式的回调函数被执行了!
//消息对象:(Body:'message confirm...' MessageProperties [headers={}, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0])
//返回编码(错误码):312
//错误信息:NO_ROUTE
//交换机:test_exchange_confirm
//路由键:confirm111
}
});
//发送消息
rabbitTemplate.convertAndSend( "test_exchange_confirm" , "confirm" , "message confirm..." );
}
}
返回模式的回调函数被执行了!
消息对象:(Body:'message confirm...' MessageProperties [headers={}, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0])
返回编码(错误码):312
错误信息:NO_ROUTE
交换机:test_exchange_confirm
路由键:confirm111
3 两种消息可靠性传递总结
1 confirm确认模式
1 在connectionFactory中设置开启确认模式.默认是false不开启的 publisher-confirms="true"
2 使用rabbitTemplate.setConfirmCallBack()设置回调函数,当消息发送到exchange交换机后回调confirm方法,在方法中判断ack,如果为true,发送成功,false发送失败,需要进行处理
2 return退回模式
1 在connectionFactory中设置开启回退模式.默认是false不开启的 publisher-returns="true"
2 使用rabbitTemplate.setReturnCallBack()设置回调函数,当消息从exchange路由到queue失败后,如果设置了rabbitTemplate.setMandatory(true);则会将消息退回给producer,并执行回调函数returnedMessage
3 在rabbitMQ中也提供了事务机制,但是性能比较差,不推荐使用
使用channel下列方法,完成事务控制
txSelect() 用于将当前channel设置成transaction模式
txCommon() 用于提交事务
toRollback() 用于回滚事务