1. Introduction to the activity Center scene

In the early stage of the online e-commerce system, there are often some “new” activities, such as the activity department proposing the registration of new users to give points and coupons.

Based on the design concept of distributed and microservice, the usual architecture design (subsystem interaction) is shown in the figure below:

Its core system is described as follows:

  • The account center provides services such as user login and user registration. When a new user registers, a message is sent to the USER_REGISTER subject in the MQ server. The main process ends and is decouples from the process of sending points and coupons.

  • Coupons (coupon system) provides basic services related to coupons, such as issuing coupons and using coupons.

  • Integral center provides integral related services, such as integral gift, integral consumption, integral inquiry and other basic services.

  • The points delivery service (consumer) subscribes to MQ and decides whether points need to be donated according to the rules. If so, it calls the basic interface related to points to complete the issue of points.

  • Coupons are sent (consumers) to subscribe to MQ, the rules determine whether coupons need to be given, and if so, the underlying interface associated with the coupon system is invoked to complete the coupon issuance.

The above architecture design is elegant, but not watertight, and the reader must be wondering what happens if a new user registers and the message fails to send to MQ, or if the message fails to send to MQ but the system fails to register after sending MQ.

The above problem is a classic distributed transaction problem: how to ensure consistency between the two distributed operations of user registration (database operations) and MQ message sending.

RocketMQ transaction messages shine.

2. Transaction message implementation principle

In a word: The problem to be solved by RocketMQ transaction messages is the consistency of message delivery and business. The solution idea is as follows: two-stage commit and transaction status check. The specific implementation process is shown in the figure below:

Its core design concept:

  • The application starts a database transaction, performs database operations, and sends a PREPARE message in the transaction. After the PREPARE message is successfully sent, the application is notified to record the status of the local transaction and then commit the local transaction.

  • When RocketMQ receives a PREPARE message, it backs up the original message subject and the original message consumption queue, and stores the message in the message queue with the topic RMQ_SYS_TRANS_HALF_TOPIC. As a result, the prepared message is not consumed by the client.

  • The Broker message server starts a scheduled task to process messages in RMQ_SYS_TRANS_HALF_TOPIC. It sends a transaction status query request to the message sender at specified intervals, asking the message sender whether the client’s local transaction has been successful, and then decides whether to commit or roll back based on the status check. The commit or rollback operation is performed in the PREPARE state.

  • If the sender knows that the transaction is successful, it can return COMMIT. The server commits the message by restoring the topic and queue of the original message and sending it back to the Broker for consumption.

  • If the sender cannot know the transaction status clearly, it returns to UNOWN. In this case, the server waits for a certain period of time and asks the sender again. By default, the server asks the sender 15 times.

  • If the sender knows very clearly that the transaction failed, it can return ROLLBACK.

In practice, when the sender cannot obtain the transaction status, it does not arbitrarily return ROLLBACK. Instead, it returns to UNOWN and asks the server to retry periodically, as described below:

After sending a PREPARE message to the Broker, the server may initiate a transaction query before the local transaction has been committed. To avoid invalid transaction callback mechanism, RocketMQ usually does not initiate the first transaction callback until at least 6 seconds after receiving the PREPARE message. This can be configured using transactionTimeOut. Therefore, when the client fails to prove the transaction status, it should not return ROLLBACK but UNOWN.

3, business information combat

Without further ado, let’s take a look at how transaction messages are used using a scenario where a new user signs up to send coupons.

The responsibilities of the project module are described as follows:

The core code of the transaction message is assembled in transaction-Service, and its core class diagram is as follows:

The key points are as follows:

  • The UserServiceImpl Dubbo interface business implementation class, similar to MVC’s control layer, does some parameter validation, but does not perform the specific business logic, just sends a transaction message to MQ.

  • UserRegTransactionListener transaction listener, execute the business logic in the executeLocalTransaction method, local transaction database in this method.

Note: Local transactions are not executed in UserServicveImpl because exceptions thrown from executeLocalTransaction are caught by the RocketMQ framework and cannot be sensed by UserServiceImpl to achieve transaction consistency.

Next, I’ll show you the core code. All the source code has been uploaded to github repository.

Warehouse address: github.com/dingwpmz/ro…

3.1 UserServiceImpl core implementation

The core points of UserServiceImpl are as follows:

  • The parameters should be validated first, the business logic should be validated, and if the business conditions are not met, invalid messages will be sent to MQ, which will not cause a business exception but will cost performance

  • To send transaction messages, it is recommended to set a Key for the message. The value of the Key can be either a business process serial number (which uniquely identifies the business operation) or a core business field (such as an order number).

  • The business entry class can determine whether a business has failed by the transaction message sending status.

3.2 UserRegTransactionListener core implementation

The transaction listener needs to implement both the local transaction and transaction callback interfaces.

3.2.1 implementation executeLocalTransaction

First, you need to implement the executeLocalTransaction method, which executes local transactions, as shown below:

A few key points are as follows:

  • Add the database transaction label to the method.

  • Performing business logic, the example Demo simply stores user data to a database.

  • If the service fails to execute, it can be explicitly told that it needs to be rolled back, and upper-layer callers can process it according to ROLLBACK_MESSAGE.

  • If the business succeeds, it is not recommended to return COMMIT directly. Instead, it is recommended to return UNKNOW because this method, although in the last line of the method, may have an exception such as a power outage and the database did not succeed.

3.2.2 implementation checkLocalTransaction

Second, we need to implement the transaction status check, which is used by the RocketMQ server to sense whether the transaction is successful. The implementation principle is shown in the following figure:

The key points are as follows:

  • If it is clear that the local transaction succeeded, COMMIT_MESSAGE is returned

  • If the local transaction is not confirmed, ROLLBACK_MESSAGE cannot be returned, but an UNKNOW message is returned, and the server waits for the next transaction check (not triggered immediately). By default, the server checks the transaction for 15 times. If UNKNOW is found for 15 times, the message will be rolled back.

3.3 Code Acquisition

The above is just an interpretation of the core code of transaction messages, and focuses on the key points of implementation of each step. Based on SpringBoot, the author tries to learn the use skills of RocketMQ in combination with the scenario, and the code has been uploaded to github repository.

Github.com/dingwpmz/ro…

Click the jumpGo to the code repository.

This article is introduced here, please light up to see, like, forward, message is the biggest encouragement to me.

TOP5 best articles of the year

Here’s some advice from a 10-year IT veteran for new employees

“I” was flattered by Ali Baba

How can programmers increase influence

How to read source code efficiently

Another way FOR me to get involved in the RocketMQ open source community

Welcome to pay attention to “middleware interest circle”, reply to [column] to get 12 JAVA mainstream middleware source analysis column, reply to PDF can get massive learning materials, bytedance latest interview questions, quickly advanced to fight strange, to achieve a breakthrough in the workplace.

Middleware interest circle

RocketMQ Technology Insider author maintenance, mainly into the system analysis of JAVA mainstream middleware architecture and design principles, to build a complete Internet distributed architecture system, help break the workplace bottleneck.