爲什麼存在確認機制
根據官網翻譯過來:
根據定義,使用消息傳遞代理(如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方法