为什么存在确认机制
根据官网翻译过来:
根据定义,使用消息传递代理(如rabbitmq)的系统是分布式的。由于发送的协议方法(消息)不能保证到达对等方或被对等方成功地处理,因此发布者和消费者都需要一种传递和处理确认的机制。rabbitmq支持的几个消息传递协议提供了这样的特性。
它们对于从发布服务器到rabbitmq节点以及从rabbitmq节点到消费者的可靠交付都是必不可少的。换言之,它们对于数据安全至关重要,应用程序和rabbitmq节点一样负责数据安全。
发送端确认和失败回调
要确保生产者生产的消息成功到达队列,有两个步骤,生产者到交换机,交换机到队列
发送端确认是要确保生产者生产的消息成功到达交换机
失败回掉是消息从交换机成功路由到队列
保证消息成功到达交换机有两种方式
1.通过AMOP提供的事务机制
保证消息不会丢失的唯一方法是使用事务——使通道具有事务性,然后对每个消息或消息集发布、提交。在这种情况下,事务是不必要的重量级,并将吞吐量减少250倍。为了补救这一点,引入了确认机制。它模拟了协议中已经存在的使用者确认机制。
配置类需要开启声明式事务,RabbitTemplate设置开启事务,AMOP事务不能和确认机制同时存在
RabbitTemplate的使用案例(同步),由调用者提供外部事务,在模板中配置了channe-transacted=true。通常是首选,因为它是非侵入性的(低耦合)
@Configuration
@EnableTransactionManagement
public class RabbitmqConfig {
@Bean
public RabbitTransactionManager rabbitTransactionManager(ConnectionFactory connectionFactory){
RabbitTransactionManager rabbitTransactionManager = new RabbitTransactionManager(connectionFactory);
return rabbitTransactionManager;
}
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
//ip
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("helloWorld");
connectionFactory.setPassword("helloWorld");
connectionFactory.setVirtualHost("testHost");
return connectionFactory;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
//开启事务
rabbitTemplate.setChannelTransacted(true);
return rabbitTemplate;
}
@Bean
public DirectExchange defaultExchange() {
return new DirectExchange("directExchange");
}
@Bean
public Queue queue() {
//名字 是否持久化
return new Queue("testQueue", false);
}
@Bean
public Binding binding() {
//绑定一个队列 to: 绑定到哪个交换机上面 with:绑定的路由建(routingKey)
return BindingBuilder.bind(queue()).to(defaultExchange()).with("direct.key");
}
}
@Component
public class MessageSender {
@Autowired
RabbitTemplate rabbitTemplate;
@Transactional
public void send(){
CorrelationData correlationData = new CorrelationData("订单ID");
rabbitTemplate.convertAndSend("directExchange","direct.key","hello",correlationData);
System.out.println(1/0);
}
}
事务开启的时候,同一个事务中有异常消息不会发送到消息队列
去掉@Transactionl或者不设置rabbitTemplate中的事务,消息会发送到队列
但是事务的方式官网上不推荐,太影响性能了
2.开启确认模式
若要启用“确认”,客户端将发送confirm.select方法。根据是否设置了等待,代理可以用确认。一旦在通道使用confirm.select方法,就称其为confirm模式。事务通道不能置于确认模式,并且一旦通道处于确认模式,就不能使其成为事务通道。
@Configuration
public class RabbitmqConfig {
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
//ip
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("helloWorld");
connectionFactory.setPassword("helloWorld");
connectionFactory.setVirtualHost("testHost");
//发送方确认
connectionFactory.setPublisherConfirms(true);
connectionFactory.setPublisherReturns(true);
return connectionFactory;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMandatory(true);
//确认回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
System.out.println(b);
System.out.println(s);
System.out.println(correlationData);
}
});
//失败回调
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int i, String s, String s1, String s2) {
System.out.println(message);
System.out.println(s);
System.out.println(s1);
System.out.println(s2);
}
}
});
return rabbitTemplate;
}
@Bean
public DirectExchange defaultExchange() {
return new DirectExchange("directExchange");
}
@Bean
public Queue queue() {
//名字 是否持久化
return new Queue("testQueue", false);
}
@Bean
public Binding binding() {
//绑定一个队列 to: 绑定到哪个交换机上面 with:绑定的路由建(routingKey)
return BindingBuilder.bind(queue()).to(defaultExchange()).with("direct.key");
}
}
@Component
public class MessageSender {
@Autowired
RabbitTemplate rabbitTemplate;
public void send(){
CorrelationData correlationData = new CorrelationData("订单ID");
rabbitTemplate.convertAndSend("directExchange","direct.key","hello",correlationData);
}
}
生产者发送消息后如果消息到达exchange,就会回调confirm方法,ack为true
如果消息没有到达exchange,就会回调confirm方法,ack为false
exchange路由到queue成功就不会回调return方法
exchange路由到queue不成功就会回调return方法