備忘錄五:Spring Boot + RabbitMQ 分散式事務
一:分散式事務解決方案
1.兩階段提交(2PC)
第一階段:事務協調器要求每個涉及到事務的資料庫預提交(precommit)此操作,並反映是否可以提交.
第二階段:事務協調器要求每個資料庫提交資料。
案例可參照 http://blog.itpub.net/28624388/viewspace-2137095/
2.補償事務(TCC)
TCC 其實就是採用的補償機制,其核心思想是:針對每個操作,都要註冊一個與其對應的確認和補償(撤銷)操作。它分為三個階段:
Try 階段主要是對業務系統做檢測及資源預留
Confirm 階段主要是對業務系統做確認提交,Try階段執行成功並開始執行 Confirm階段時,預設 Confirm階段是不會出錯的。即:只要Try成功,Confirm一定成功。
Cancel 階段主要是在業務執行錯誤,需要回滾的狀態下執行的業務取消,預留資源釋放。
3.本地訊息表(非同步確保)
本地訊息表這種實現方式應該是業界使用最多的,其核心思想是將分散式事務拆分成本地事務進行處理。
基本思路:
a.訊息生產方,需要額外建一個訊息表,並記錄訊息傳送狀態。訊息表和業務資料要在一個事務裡提交,也就是說他們要在一個資料庫裡面。然後訊息會經過MQ傳送到訊息的消費方。如果訊息傳送失敗,會進行重試傳送。
b. 訊息消費方,需要處理這個訊息,並完成自己的業務邏輯。此時如果本地事務處理成功,表明已經處理成功了,如果處理失敗,那麼就會重試執行。如果是業務上面的失敗,可以給生產方傳送一個業務補償訊息,通知生產方進行回滾等操作。
c.生產方和消費方定時掃描本地訊息表,把還沒處理完成的訊息或者失敗的訊息再傳送一遍。如果有靠譜的自動對賬補賬邏輯,這種方案還是非常實用的。
二:Spring Boot + RabbitMQ分散式事務實現
1.pom.xml依賴配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.application.yaml rabbitmq配置
# RabbitMQ
rabbitmq:
host: 112.74.105.178
port: 5672
username: admin
password: admin
virtual-host: /
publisher-confirms: true
publisher-returns: true
listener:
simple:
acknowledge-mode: manual
3.RabbitMQConfig.java
@Configuration
public class RabbitMQConfig {
// 下單並且派單存佇列
public static final String ORDER_DIC_QUEUE = "order_dis_queue";
// 補單佇列,判斷訂單是否已經被建立
public static final String ORDER_CREATE_QUEUE = "order_create_queue";
// 下單並且派單交換機
private static final String ORDER_EXCHANGE_NAME = "order_exchange_name";
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(new Jackson2JsonMessageConverter());
return template;
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
return factory;
}
@Bean
public Queue OrderDicQueue() {
return new Queue(ORDER_DIC_QUEUE);
}
@Bean
public Queue OrderCreateQueue() {
return new Queue(ORDER_CREATE_QUEUE);
}
@Bean
DirectExchange directOrderExchange() {
return new DirectExchange(ORDER_EXCHANGE_NAME);
}
@Bean
Binding bindingExchangeOrderDicQueue() {
return BindingBuilder.bind(OrderDicQueue()).to(directOrderExchange()).with("orderRoutingKey");
}
@Bean
Binding bindingExchangeOrderCreateQueue() {
return BindingBuilder.bind(OrderCreateQueue()).to(directOrderExchange()).with("orderRoutingKey");
}
}
4. 訊息生產者
public class MsgPushInfoServiceImpl extends ServiceImpl<MsgPushInfoMapper, MsgPushInfoEntity>
implements MsgPushInfoService, RabbitTemplate.ConfirmCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
public void orderAndDsipatch() {
try {
String orderId = "123456";
JSONObject jsonObect = new JSONObject();
jsonObect.put("orderId", orderId);
String msg = jsonObect.toString();
System.out.println("msg:" + msg);
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/json");
messageProperties.setMessageId(orderId);
Message message = new Message(msg.getBytes(),messageProperties);
CorrelationData correlationData = new CorrelationData(orderId);
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.convertAndSend("order_exchange_name", "orderRoutingKey", message, correlationData);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
String orderId = correlationData.getId();
System.out.println("訊息id:" + orderId);
if (ack) { // 訊息傳送成功
System.out.println("訊息傳送確認成功");
} else {
// 重試機制
System.out.println("訊息傳送確認失敗:" + cause);
}
}
}
5.訊息消費者
@Component
public class DispatchReceiver {
@RabbitHandler
@RabbitListener(queues = "order_dis_queue", containerFactory = "rabbitListenerContainerFactory")
public void process(Message message, Channel channel) {
System.out.println("rev : " + message.getMessageProperties().getMessageId());
try {
System.out.println("======basicNack====="+message.getMessageProperties().getDeliveryTag());
//業務處理成功,則刪除訊息
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
//業務處理失敗,則傳送補償訊息
} catch (Exception e) {
e.printStackTrace();
}
}
}
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28624388/viewspace-2653860/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- RabbitMQ備忘錄MQ
- 使用Spring Boot實現分散式事務Spring Boot分散式
- Spring Boot 整合 Seata 解決分散式事務問題Spring Boot分散式
- 備忘錄一:Spring Boot HikariCP 配置說明Spring Boot
- 備忘錄二:Spring Boot Actuator+Prometheus+GrafanaSpring BootPrometheusGrafana
- 備忘錄四:Spring Boot + P6SpySpring Boot
- 使用Kafka Streams和Spring Boot微服務中的分散式事務 - PiotrKafkaSpring Boot微服務分散式
- 備忘錄六:Spring Boot + Swagger_UISpring BootSwaggerUI
- 分散式事務之Spring事務與JMS事務(二)分散式Spring
- 分散式事務(五)之最大努力通知分散式
- 分散式鎖和spring事務管理分散式Spring
- 面試必備的分散式事務方案面試分散式
- 分散式事務(一)—分散式事務的概念分散式
- 備忘錄九:Spring Boot+Shiro許可權管理Spring Boot
- 備忘錄三:Spring Boot+Druid+log4j2Spring BootUI
- 10、Spring Boot分散式Spring Boot分散式
- Spring boot +mybatis 實現宣告式事務管理Spring BootMyBatis
- 五(二)、spring 宣告式事務xml配置SpringXML
- 分散式事務(3)---RocketMQ實現分散式事務原理分散式MQ
- 分散式事務解決方案(五)【TCC型方案】分散式
- 分散式事務和分散式hash分散式
- 分散式事務(4)---RocketMQ實現分散式事務專案分散式MQ
- XA式、非XA式Spring分散式事務的實現Spring分散式
- 使用Spring Boot + Kafka實現Saga分散式事務模式的原始碼 - vinsguruSpring BootKafka分散式模式原始碼
- 分散式事務概述分散式
- 理解分散式事務分散式
- 分散式事務--CAP分散式
- 【ITOO】--分散式事務分散式
- WS分散式事務分散式
- oracle分散式事務Oracle分散式
- 聊聊分散式事務分散式
- seata 分散式事務分散式
- 奈學教你五分鐘學會分散式事務分散式
- 五大分散式事務,你瞭解多少?分散式
- 五種分散式事務解決方案(圖文總結)分散式
- 分散式系統(三)——分散式事務分散式
- 分散式事務~從seata例項來學習分散式事務分散式
- Spring分散式事務XA事務(兩段提交2PC)實現Spring分散式