1消息的可靠性传递
使用rabbitMQ时,消息的发送方希望杜绝然和消息丢失的失败场景。RabbitMQ为我们提供了两种方式来控制消息的可靠性传递模式
- confirm 确认模式
- return 退回模式
rabbitMQ消息的传递过程-->
producer-->rabbbimq broker -->exchange-->queue-->consumer
生产者将消息传递给rabbitMQ broker中间件之后,会放入到exchange中,再经过路由信息,将消息放入到指定的队列中,消费者监听指定的队列,来获取消息。
- confirm 确认模式指的是消息从producer到exchange过程中都会调用一个回调函数confirmCallback。如果消息成功收到则返回true,如果没成功收到则返回false(不管消息是都否都成功到达,都会调用回调函数)
- return 从exchange到queue中,消息传递失败则会返回一个returnCallback
利用这两个Callback控制消息的可靠性传递
spring整合rabbitmq链接:
1.1实现confirm确认:
定义交换机以及指定队列
<!-- 加载配置类-->
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<!-- 定义rabbitmq 连接工厂-->
<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对象操作可以在代码中方便发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
<!-- 消息的可靠性投递(生产端)-->
<rabbit:queue id="test_queue_confirm" name="test_queue_confirm"/>
<!-- 定义交换机-->
<rabbit:direct-exchange name="test_exchange_confirm">
<rabbit:bindings>
<rabbit:binding queue="test_queue_confirm" key="confirm"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
注意开启confirm和return需要开启publisher-confirms="true" publisher-returns="true"配置。
模拟生产者发送消息:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class Producer {
//1.注入RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 确认模式:
* 步骤:
* 1.开启确认模式:ConnectionFactory中开启publisher-confirms="true"
* 2.在rabbitTemplate定义ConfirmCallBack回调函数
*/
@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) {
System.out.println("confirm方法被执行了....");
if(ack){
//接收成功
System.out.println("消息接收成功");
}else {
//接收成功
System.out.println("消息接收失败"+cause);
//处理一下,让消息再次发送
}
}
});
//3.发送消息
rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message confirm....");
}
}
1.2实现returnCallback:
public class Producer {
//1.注入RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
/**
*回退模式:当消息发送给Exchange后,Exchange路由到Queue失败时,才会执行ReturnCallBack
* 步骤:
* 1.开启回退模式
* 2.设置ReturnCallBack
* 3.设置Exchange处理消息的模式:
* 1.如果消息没有路由到Queue,则丢弃消息(默认)
* 2.如果消息没有路由到Queue,则返回给消息的发送方ReturnCallBack
*/
@Test
public void testReturn(){
//设置交换机处理消息失败的模式
rabbitTemplate.setMandatory(true);
/**
* message :消息对象
* replyCode :错误码
* replyText :错误信息
* exchange :交换机
* routingKey:路由键
*/
//2.设置returnCallBack
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, 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....");
}
}
只有在找不到路由名字写错或者程序内部出现问题时,才会执行returnCallBack方法
1.3消息的可靠投递小结
设置ConnectionFactory的publisher-confirms="true" 开启确认模式
使用rabbitTemplate.setConfirmCallback设置函数回调,消息发到交换机后回调函数,
ack为true,则发送成功,ack为false,则发送失败。
设置ConnectionFactory的publisher-returns="true" 开启退回模式
使用rabbitTemplate.setReturnCallback 设置回退函数,当消息从路由到队列失败时,如果设置了rabbitTemplate.setMandatory(true),会将消息退回给producer,并执行回调函数
1.4Consumer 确认
ack指Acknowledge。表示消费端收到消息后确认方式
有三种确认方式:
- 自动确认:acknowledge = "none" (收到消息后自动确认,若程序是否出现异常可能会丢失消息)
- 手动确认:acknowledge = "manual" (收到消息后,手动确认,如果出现异常,可以请求消息队列重新发送)
- 根据异常情况确认:acknowledge = "auto"(使用情况很复杂,根据抛异常的类型做处理)
自动确认消息一旦呗Consumer接收到,则自动确认,将相应的message从RabbitMQ消息缓存中移除,如果业务处理出现异常,则会丢失消息。
手动确认业务处理成功后调用channel.basicAck()手动签收,如果出现异常,则调用channel.basicNack()方法,让消息自动重新发送。
手动确认
客户端设置手动确认,业务逻辑正常处理完之后,手动确认签收,否则拒绝签收数据,要求broker重新发送数据。
@Component
public class AckListener implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
Thread.sleep(1000);
long deliveryTag = message.getMessageProperties().getDeliveryTag();
System.out.println("~~~~~~~~");
try {
//1.接受转换消息
System.out.println(new String(message.getBody()));
//2.处理业务逻辑
System.out.println("处理业务逻辑。。。。");
// int i = 3/0;
//3.手动签收
/**
* deliveryTag:消息标签
* true:可以同时处理多条消息
*/
channel.basicAck(deliveryTag, true);
}catch (Exception e){
//4.拒绝签收
/**
* 第三个参数:设置为true,则消息重新回到queue,broker会重新发送消息给消费端
*/
channel.basicNack(deliveryTag,true,true);
}
}
}