The background,
In microservices architecture, we often use asynchronous means to improve system throughput and decouple upstream and downstream, and the most common way to build asynchronous architecture is to use message queue (MQ). How can asynchronous architecture achieve data consistency? This article focuses on using RocketMQ transaction messages to address consistency issues.
RocketMQ is alibaba’s open source distributed messaging middleware, which has become a top project of Apache. After repeated trials of massive messages on Tmall Double 11, it has the characteristics of high performance, low latency and high reliability
PS: How to ensure consistency in synchronization scenarios? See the article “How to do distributed Transactions in Spring Cloud Synchronization Scenarios? Try Seata.”
Second, MQ selection
You can see that RocketMQ is superior to its competitors in terms of business processing and supports transactional messaging natively
PS: What if the business system uses other MQ products but needs transaction messages? Learning principles to develop their own implementation!
What is a transaction message
For example, the following scenario: Generate order record -> MQ -> Add credits
Should we create the order record first or send the MQ message first?
-
Send the MQ message first: this is obviously a no-no, because there is no way to retrieve the message if the message is sent successfully and the order creation fails
-
Create the order record first: throw an exception if the MQ message fails to be sent after the order is successfully created, because the order data can be rolled back because both operations are in a local transaction
The above way two seems to be no problem, but the network is unreliable! If the MQ response is not received for network reasons, a rollback is performed in the face of uncertain results; However, the MQ server did receive the message, but the response back to the client was lost! So transaction messages are used to ensure atomicity between local transactions and MQ message sending!
RocketMQ Transaction message principle
The main logic is divided into two flows:
- Transaction message sending and submission:
- send
Half the message
MQ server
The response message writes the result- Execute according to the sent result
Local transactions
If the write fails, then the half message to the businessinvisible, local logic is not executed) - Execute based on local transaction state
Commit
orRollback
The Commit operation generates the message index, the message to consumervisible)
- send
- To check the process:
- For a long time no
Commit/Rollback
Transaction message (pending
Status message), sent once from the serverBack to check Producer
After receiving the check back message, check corresponding to the check back messageLocal transaction status
- According to the local transaction status, re
Commit
orRollback
- For a long time no
Logical sequence diagram
Five, asynchronous architecture consistency implementation ideas
As you can see from the above principle, transaction messages only guarantee the atomicity of the local transaction and MQ message delivery as a whole. Once delivered to the MQ server, there is no guarantee that the consumer will be able to consume successfully! If the consumer fails to consume, it is recommended to record the exception information and handle it manually. It is not recommended to roll back the data of the upstream service (because they are decoupled and too complex). We can use two features of MQ, retry and dead-letter queue, to assist the consumer:
- A certain number of times after a consumption failure
retry
- If the retry fails, the message is thrown in
Dead-letter queue
里 - Another thread listens for consumption
Dead-letter queue
Messages, log and alert!
The consumer needs to implement idempotency because of retry
Sample distributed transaction scenario
Use the scenario just mentioned: Generate order records -> MQ -> increase credits; Let’s talk briefly about how to do this in Spring Cloud. Please download the demo for details. Apache RocketMQ Message Queue Deployment and Visual Interface Installation
6.1. Introduce dependencies
Use the Spring-Cloud-Stream framework to access RocketMQ
Spring Cloud Stream is a message-driven framework for decoupling applications from MQ message queues through abstract definitions. It currently supports RabbitMQ, Kafka, and RocketMQ
6.2. Enable transaction messages
Message producers need to add transactional: True to enable transaction messages
6.3. The order service sends the half message
The half message sent here is invisible to the consumer because transaction messaging is enabled
6.4. The order service listens for the half message
Using the @ RocketMQTransactionListener annotations to monitor messages, and implement RocketMQLocalTransactionListener interface, this interface has two methods
- ExecuteLocalTransaction: Used to commit local transactions
- CheckLocalTransaction: Used for transaction backcheck
If the transaction message fails to commit, it takes about 1 minute for the transaction callback method to be invoked
6.5. Points service consumption message
Note: Since there are retries, the real business here needs to implement idempotency itself
6.6. Consumption dead letter queue warning
Monitors and consumes messages in the dead-letter queue, logs errors, and notifies o&M personnel
6.7. Test cases
Demo provides three interfaces to test different scenarios:
-
Transaction is successful
http://localhost:11002/success
The process is as follows:
- Order created successfully
- The transaction message was committed successfully
- Consumption message added credits successfully
-
The order was created successfully but failed to commit the transaction message
http://localhost:11002/produceError
The process is as follows:
- Order created successfully
- Failed to commit transaction message
- The transaction rollback succeeds (wait about 1 minute)
- The transaction message was committed successfully
- Consumption message added credits successfully
-
Consumption message failure
http://localhost:11002/consumeError
The process is as follows:
- Order created successfully
- The transaction message was committed successfully
- Failed to add credits to consume message
- Failed to retry consumption message
- Succeeded in entering the dead letter queue
- The message consuming the dead-letter queue succeeded
- Logs are logged and alarms are generated successfully
7. Demo download address
Gitee.com/zlt2000/mic…
Recommended reading
- Log troubleshooting difficulty? Distributed log link tracing to help you
- Zuul integrates Sentinel’s latest gateway flow control components
- Ali Registry Nacos production deployment solution
- Spring Boot custom configuration items implement automatic prompts in the IDE
- How to do Spring Cloud Zuul dynamic routing? Integrating the Nacos implementation is simple
- How can Spring Cloud developers resolve service conflicts and instance hopping?
- How to do distributed transactions in Spring Cloud synchronization scenarios? Try Seata
Scan code attention has surprise!