E-commerce project part09 Distributed transaction Seata&MQ reliable news

Distributed transactions

In the microservice architecture, completing a certain business function may require operating multiple databases across multiple services. This involves distributed transactions,需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。

In the e-commerce project, two distributed transaction solutions will be explained based on the order business focus:

  • 2PC solution: Implemented based on Seata AT
  • mq reliable message solution: based on Rocketmq transaction message implementation

Seata architecture

In Seata's architecture, there are three roles:

  • TC (Transaction Coordinator) - The transaction coordinator
    maintains the status of global and branch transactions, and drives global transaction commit or rollback.
  • TM (Transaction Manager) - Transaction Manager
    defines the scope of global transactions: start global transactions, commit or rollback global transactions.
  • RM (Resource Manager) - Resource Manager
    manages resources for branch transaction processing, talks to TC to register branch transactions and report the status of branch transactions, and drives branch transactions to commit or rollback.

其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。

In Seata, the life cycle of a distributed transaction is as follows:

  1. TM requests TC to start a global transaction. TC will generate an XID as the number of the global transaction. The XID will be propagated in the call link of the microservice to ensure that the sub-transactions of multiple microservices are associated together.
  2. RM requests TC to register the local transaction as a branch transaction of the global transaction, which is associated through the XID of the global transaction.
  3. TM requests TC to tell XID whether to commit or rollback the global transaction corresponding to it.
  4. TC drives RMs to commit or roll back their own local transactions corresponding to XID.
    (branch table, lock table, log table)
    Insert image description here

Distributed transactions combined with sub-database and sub-table

ShardingSphere整合Seata

Introduce dependencies

<!‐‐shardingsphere整合seata依赖‐‐>
<dependency>
  <groupId>org.apache.shardingsphere</groupId>
  <artifactId>sharding‐transaction‐base‐seata‐at</artifactId>
  <version>4.1.1</version>
</dependency>

When an application that configures seata.conf
to include Seata flexible transactions starts, the data source configured by the user will be adapted to the
DataSourceProxy required for Seata transactions based on the configuration of seata.conf and registered in RM.

client {
    
    
  application.id = tulingmall‐order‐curr
  transaction.service.group = tuling‐order‐group
}

Enable global transaction configuration
core classSeataATShardingTransactionManager

//全局事务交给SeataATShardingTransactionManager管理
@ShardingTransactionType(TransactionType.BASE)
@Transactional
public CommonResult generateOrder(OrderParam orderParam, Long memberId) {
    
    
...
}

Insert image description here

Notice:GlobalTransactional和ShardingTransactionType不能同时出现,此处不能使用@GlobalTransactional。同时需要关闭数据源自动代理

 seata:
  enable‐auto‐data‐source‐proxy: false #关闭数据源自动代理,交给sharding‐jdbc那边
  

Implementation of reliable message eventual consistency scheme

可靠消息最终一致性方案是指当事务发起执行完成本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发给事务参与方最终事务要达到一致。

RocketMQ保证消息不丢

Production side: synchronization message (ACK mechanism)

Storage side: Synchronous disk flashing

Consumer side: manual offset

Rocketmq transaction message implementation

The RocketMQ transaction message design is mainly to solve the atomicity problem of message sending and local transaction execution on the Producer side. The two-way communication capabilities between the broker and the producer side in RocketMQ's design enable the broker to naturally exist as a transaction coordinator; and RocketMQ itself provides The storage mechanism provides persistence capabilities for transaction messages; RocketMQ's high availability mechanism and reliable message design ensure that transaction messages can still ensure the final consistency of the transaction when an exception occurs in the system.

After RocketMQ 4.3, complete transaction messages were implemented. In fact, it is an encapsulation of the local message table. The local message table is moved inside MQ to solve the atomicity problem of message sending and local transaction execution on the Producer side.
Insert image description here

The execution process is as follows:

1) Producer sends a transaction message.
Producer (MQ sender) sends a transaction message to MQ Server. MQ Server marks the message status as Prepared (preview status). Note that this message consumer (MQ subscriber) cannot consume it at this time. .
2) MQ Server responds that the message is sent successfully.
MQ Server receives the message sent by Producer and then responds that the message is sent successfully, indicating that MQ has received the message.
3) Producer executes local transactions
Producer side executes business code logic, controlled by local database transactions.
4) Message delivery
If the Producer's local transaction is executed successfully, it will automatically send a commit message to the MQ Server. After receiving the commit message, the MQ Server will consume the message. If the consumption is successful, it will respond ack to MQ, otherwise it will receive the message repeatedly. Here ack responds automatically by default, that is, the program automatically responds to ack when it is running normally.

The above main process has been implemented by RocketMQ. It only needs to implement local transaction execution and local transaction review methods respectively, so you only need to pay attention to the execution status of local transactions.

RocketMQ provides the RocketMQLocalTransactionListener interface:

public interface RocketMQLocalTransactionListener {
    
    
/**
  * 发送prepare消息成功此方法被回调,该方法用于执行本地事务
  * @param msg 回传的消息,利用transactionId即可获取到该消息的唯一Id
  * @param arg 调用send方法时传递的参数,当send时候若有额外的参数可以传递到send方法中,这里能获取到
  *@return 返回事务状态,COMMIT :提交 ROLLBACK :回滚 UNKNOW :回调
  */
	RocketMQLocalTransactionState executeLocalTransaction(Message msg,Object arg);
/**
 * @param msg 通过获取transactionId来判断这条消息的本地事务执行状态
 * @return 返回事务状态,COMMIT :提交 ROLLBACK :回滚 UNKNOW :回调
 */
 	RocketMQLocalTransactionState checkLocalTransaction(Message msg);
 }

Case

	/**
     * 使用事务消息机制发送订单
     * @return
     */
    public boolean sendCreateOrderMsg(Long orderId, Long memberId){
    
    
//        SendResult result = rocketMQTemplate.syncSend(asyncOrderTopic+":"+ORDERTAG,message);
        String destination = asyncOrderTopic+":"+ORDERTAG;
        Message<String> message = MessageBuilder.withPayload(orderId+":"+memberId)
                .build();
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(destination,message,orderId);
        return SendStatus.SEND_OK == sendResult.getSendStatus();
    }

    /**
     * 发送延时同步库存消息,60s后同步库存
     * @param productId
     * @param promotionId
     * @return
     */
    public boolean sendStockSyncMessage(Long productId,Long promotionId){
    
    
        Message message = MessageBuilder.withPayload(productId+":"+promotionId).build();
        SendResult result = rocketMQTemplate.syncSend("stock-sync",message,5000,5);
        return SendStatus.SEND_OK == result.getSendStatus();
    }

Guess you like

Origin blog.csdn.net/Forbidden_City/article/details/132623564