Table of contents
Hmily implements TCC distributed transactions_Project Construction
Hmily implements TCC distributed transaction practice_public module
Hmily implements TCC distributed transactions_integrating Dubbo framework
Transfer out the microservice to realize the transfer function
Hmily implements TCC transaction_integrate Hmily framework
Hmily realizes TCC transaction_transfer into and out of microservices to realize the Confirm stage
Realization of Reliable Message Final Consistency Distributed Transaction_Local Message Table
Hmily implements TCC distributed transactions_Project Construction
Create parent project tx-tcc
Set up logic project
<packaging>pom</packaging>
Create public modules
Create a transfer-out bank microservice
Create an incoming banking microservice
Public modules import dependencies
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
Hmily implements TCC distributed transaction practice_public module
Implementation of the persistence layer
The persistence layer of the public module of the project is shared by the transfer-out bank micro-service and the transfer-in bank micro-service. Logically, it not only realizes the processing of the transfer-out amount, but also realizes the processing of the transfer-in amount, and also realizes the TCC distributed transaction Each stage performs record saving and query operations.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserAccountDto implements Serializable {
private static final long serialVersionUID = 3361105512695088121L;
/**
* 自定义事务编号
*/
private String txNo;
/**
* 转出账户
*/
private String sourceAccountNo;
/**
* 转入账户
*/
private String targetAccountNo;
/**
* 金额
*/
private BigDecimal amount;
}
Definition of Dubbo interface
In the implementation process of the whole project, the remote interface call is realized through Dubbo between the transfer-out bank microservice and the transfer-in bank microservice. Because the Dubbo interface defined in the project needs to be referenced by both the transfer-out bank microservice and the transfer-in bank microservice, the Dubbo interface needs to be placed in the public module of the project.
public interface UserAccountBank02Service {
/**
* 转账
*/
void transferAmountToBank2(UserAccountDto userAccountDto);
}
Hmily implements TCC distributed transactions_integrating Dubbo framework
The transfer-in bank microservice provides the Dubbo interface of the transfer-in account. When the transfer-out bank microservice calls the Dubbo interface of the transfer-in bank microservice, the transfer-in bank microservice will perform the operation of increasing the account balance.
Introduce Dubbo dependency
<!-- dubbo依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.15</version>
</dependency>
<!--ZooKeeper客户端-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.2.1</version>
</dependency>
Transfer to bank microservices and write application.yml
server:
port: 6005
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.66.100:3306/tx-tcc-bank02?
useUnicode=true&characterEncoding=utf8
username: root
password01: 123456
dubbo:
scan:
base-packages: com.itbaizhan.service
application:
name: tx-tcc-bank02
registry:
address: zookeeper://localhost:2181
protocol:
name: dubbo
port: 12345
Write application.yml for transfer-out bank microservices
server:
port: 6004
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.66.100:3306/tx-tcc-bank02?
useUnicode=true&characterEncoding=utf8
username: root
password01: 123456
dubbo:
scan:
base-packages: com.itbaizhan.service
application:
name: tx-tcc-bank01
registry:
address: zookeeper://localhost:2181
Write the main startup class transferred to the microservice
@MapperScan("com.tong.mapper")
@SpringBootApplication
@Slf4j
public class Bank2Main6005 {
public static void main(String[] args) {
SpringApplication.run(Bank2Main6005.class,args);
log.info("************ Bank2Main6005 启动成功 **********");
}
}
Write the main startup class for transferring out microservices
@MapperScan("com.tong.mapper")
@SpringBootApplication
@Slf4j
public class Bank1Main6004 {
public static void main(String[] args) {
SpringApplication.run(Bank1Main6004.class,args);
log.info("*********** Bank1Main6004*******");
}
}
Implementation of business logic layer
The business logic layer of the transfer-out bank micro-service is mainly to realize the amount deduction operation of the local account, and realize the increase operation of the corresponding account balance of the transfer-in bank micro-service through the Dubbo framework.
Transfer to microservice to realize transfer function
@DubboService(version = "1.0.0")
public class UserAccountServicelmpl implements UserAccountBank02Service {
@Autowired
private UserAccountMapper userAccountMapper;
@Override
public void transferAmountToBank02(UserAccountDTO userAccountDTO) {
// 1. 根据账户编号查询账户信息
UserAccount userAccount = userAccountMapper.selectById(userAccountDTO);
// 2. 判断账户是否存在
if (userAccount != null ){
userAccount.setAccountBalance(userAccount.getAccountBalance().add(userAccountDTO.getBigDecimal()));
// 3. 更新账户
userAccountMapper.updateById(userAccount);
}
}
}
Transfer out the microservice to realize the transfer function
Write transfer interface
/**
* 跨库转账
* @param userAccountDTO
*/
void transferAmountToBank02(UserAccountDTO userAccountDTO);
Implementation of transfer interface for transfer out microservices
@Service
public class UserAccountServiceImpl implements IUserAccountService {
@DubboReference(version = "1.0.0")
private UserAccountBank02Service userAccountBank02Service;
@Autowired
private UserAccountMapper userAccountMapper;
@Override
public void transferAmountToBank02(UserAccountDTO userAccountDTO) {
// 1. 根据账户编号查询账户信息
UserAccount userAccount = userAccountMapper.selectById(userAccountDTO);
// 2. 判断账户是否存在
if (userAccount != null && userAccount.getAccountBalance().compareTo(userAccountDTO.getBigDecimal()) > 0){
userAccount.setAccountBalance(userAccount.getAccountBalance().subtract(userAccountDTO.getBigDecimal()));
// 3. 更新账户
userAccountMapper.updateById(userAccount);
}
// 4.远程调用转入微服务账户增加金额
userAccountBank02Service.transferAmountToBank02(userAccountDTO);
}
}
Transfer out of microservices and write the control layer
@RestController
@RequestMapping("/userAccount")
public class UserAccountController {
@Autowired
private IUserAccountService iUserAccountService;
/**
* 转账
* @return
*/
@GetMapping("/transfer")
public String transfer(UserAccountDTO userAccountDTO){
iUserAccountService.transferAmountToBank02(userAccountDTO);
return "转账成功";
}
}
Hmily implements TCC transaction_integrate Hmily framework
Transferring in and out of microservices introduces dependencies
<dependency>
<groupId>org.dromara</groupId>
<artifactId>hmily-spring-boot-starter-apache-dubbo</artifactId>
<version>2.1.1</version>
<exclusions>
<exclusion>
<groupId>org.dromara</groupId>
<artifactId>hmily-repository-mongodb</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
Transfer to microservices and write hmily configuration files
Create a new file in the resource of the project named: hmily.yml configuration file
hmily:
server:
configMode: local
appName: user-account-bank02-dubbo
# 如果server.configMode eq local 的时候才会读取到这里的配置信息.
config:
appName: user-account-bank01-dubbo
serializer: kryo
contextTransmittalMode: threadLocal
scheduledThreadMax: 16
scheduledRecoveryDelay: 60
scheduledCleanDelay: 60
scheduledPhyDeletedDelay: 600
scheduledInitDelay: 30
recoverDelayTime: 60
cleanDelayTime: 180
limit: 200
retryMax: 10
bufferSize: 8192
consumerThreads: 16
asyncRepository: true
autoSql: true
phyDeleted: true
storeDays: 3
repository: mysql
repository:
database:
driverClassName: com.mysql.cj.jdbc.Driver
url :
jdbc:mysql://192.168.66.100:3306/hmily?
useUnicode=true&characterEncoding=UTF8&useOldAliasMetadataBehavior=true&autoReconnec
t=true&failOverReadOnly=false&useSSL=false&serv
erTimezone=UTC
username: root
password01: 123456
maxActive: 20
minIdle: 10
connectionTimeout: 30000
idleTimeout: 600000
maxLifetime: 1800000
Transfer out microservices and write hmily configuration files
In the resource of the project, create a new file named: hmily.yml configuration file
hmily:
server:
configMode: local
appName: user-account-bank01-dubbo
# 如果server.configMode eq local 的时候才会读取到这里的配置信息.
config:
appName: user-account-bank01-dubbo
serializer: kryo
contextTransmittalMode: threadLocal
scheduledThreadMax: 16
scheduledRecoveryDelay: 60
scheduledCleanDelay: 60
scheduledPhyDeletedDelay: 600
scheduledInitDelay: 30
recoverDelayTime: 60
cleanDelayTime: 180
limit: 200
retryMax: 10
bufferSize: 8192
consumerThreads: 16
asyncRepository: true
autoSql: true
phyDeleted: true
storeDays: 3
repository: mysql
repository:
database:
driverClassName: com.mysql.cj.jdbc.Driver
url :
jdbc:mysql://192.168.66.100:3306/hmily?
useUnicode=true&characterEncoding=UTF8&useOldAliasMetadataBehavior=true&autoReconnec
t=true&failOverReadOnly=false&useSSL=false&serv
erTimezone=UTC
username: root
password: 123456
maxActive: 20
minIdle: 10
connectionTimeout: 30000
idleTimeout: 600000
maxLifetime: 1800000
Add annotations to the implementation interface
TCC mode
Hmily implements TCC distributed transaction_transfer into and out of microservices to achieve Try phase
Transfer out of the Microservice Try phase
/**
* 转账功能
* @param userAccountDTO
*/
@HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel")
@Override
public void transferAmountToBank02(UserAccountDTO userAccountDTO) {
String txNo = userAccountDTO.getTxNo();
log.info("********** 执行bank01 的 Try 方法 ,事务id={}",txNo);
// 1、 幂等处理
TryLog tryLog = tryLogMapper.selectById(txNo);
if (tryLog != null){
return ;
}
// 2、 悬挂处理
if (confirmLogMapper.selectById(txNo) != null || cancelLogMapper.selectById(txNo) != null){
return ;
}
// 3. 根据账户编号查询账户信息
UserAccount userAccount = baseMapper.selectById(userAccountDTO.getSourceAccountNo());
// 4. 判断账户是否存在
if (userAccount != null){
// 5. 账户金额更新
LambdaUpdateWrapper<UserAccount> ulw = new LambdaUpdateWrapper<>();
// 更新转账金额
ulw.set(UserAccount::getTransferAmount,userAccount.getTransferAmount().add(userAccountDTO.getBigDecimal()));
// 更新余额
ulw.set(UserAccount::getAccountBalance,userAccount.getAccountBalance().subtract(userAccountDTO.getBigDecimal()));
ulw.eq(UserAccount::getAccountNo,userAccountDTO.getSourceAccountNo());
baseMapper.update(null,ulw);
}
// 7. 准备阶段记录
TryLog tryLog1 = new TryLog();
tryLog1.setTxNo(txNo);
tryLog1.setCreateTime(LocalDateTime.now());
tryLogMapper.insert(tryLog1);
// 8. 远程调用 转入微服务 跨库转账的功能
userAccountBank02Service.transferAmountToBank02(userAccountDTO);
}
Transfer to the microservice Try stage
/**
* 跨库转账
* @param userAccountDTO
*/
@HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel")
@Override
public void transferAmountToBank02(UserAccountDTO userAccountDTO) {
String txNo = userAccountDTO.getTxNo();
log.info("********** 执行bank02 的 Try方法 ,事务id={}",txNo);
// 1、 幂等处理
TryLog tryLog = tryLogMapper.selectById(txNo);
if (tryLog != null){
return ;
}
// 2、 悬挂处理
if (confirmLogMapper.selectById(txNo) != null || cancelLogMapper.selectById(txNo) != null){
return ;
}
// 3. 根据账户编号查询账户信息
UserAccount userAccount = userAccountMapper.selectById(userAccountDTO.getTargetAccountNo());
// 4. 判断账户是否存在
if (userAccount != null){
// 5. 账户金额更新
LambdaUpdateWrapper<UserAccount> ulw = new LambdaUpdateWrapper<>();
// 更新转账金额
ulw.set(UserAccount::getTransferAmount,userAccount.getTransferAmount().add(userAccountDTO.getBigDecimal()));
ulw.eq(UserAccount::getAccountNo,userAccountDTO.getTargetAccountNo());
userAccountMapper.update(null,ulw);
}
// 7. 准备阶段记录
TryLog tryLog1 = new TryLog();
tryLog1.setTxNo(txNo);
tryLog1.setCreateTime(LocalDateTime.now());
tryLogMapper.insert(tryLog1);
}
Hmily realizes TCC transaction_transfer into and out of microservices to realize the Confirm stage
Write the confirmation phase of the microservice that is transferred out
/**
* 确认阶段
* @param userAccountDTO
*/
public void sayConfrim(UserAccountDTO userAccountDTO) {
String txNo = userAccountDTO.getTxNo();
log.info("********** 执行bank01 的 Confrim方法 ,事务id={}",txNo);
// 1、幂等处理
ConfirmLog confirmLog = confirmLogMapper.selectById(txNo);
if (confirmLog != null){
return ;
}
// 2、根据账户id查询账户
UserAccount userAccount = baseMapper.selectById(userAccountDTO.getSourceAccountNo());
userAccount.setTransferAmount(userAccount.getTransferAmount().subtract(userAccountDTO.getBigDecimal()));
baseMapper.updateById(userAccount);
// 3、 确认日志记录
ConfirmLog confirmLog1 = new ConfirmLog();
confirmLog1.setTxNo(userAccountDTO.getTxNo());
confirmLog1.setCreateTime(LocalDateTime.now());
confirmLogMapper.insert(confirmLog1);
}
Write and transfer to the Microservice Confirm stage
/**
* 确认阶段
* @param userAccountDTO
*/
public void sayConfrim(UserAccountDTO userAccountDTO) {
String txNo = userAccountDTO.getTxNo();
log.info("********** 执行bank02 的Confrim方法 ,事务id={}",txNo);
// 1、幂等处理
ConfirmLog confirmLog = confirmLogMapper.selectById(txNo);
if (confirmLog != null) {
return;
}
// 2、根据账户id查询账户
UserAccount userAccount = userAccountMapper.selectById(userAccountDTO.getTargetAccountNo());
userAccount.setAccountBalance(userAccount.getAccountBalance().add(userAccountDTO.getBigDecimal()));
userAccount.setTransferAmount(userAccount.getTransferAmount().subtract(userAccountDTO.getBigDecimal()));
userAccountMapper.updateById(userAccount);
// 3、 确认日志记录
ConfirmLog confirmLog1 = new ConfirmLog();
confirmLog1.setTxNo(userAccountDTO.getTxNo());
confirmLog1.setCreateTime(LocalDateTime.now());
confirmLogMapper.insert(confirmLog1);
}
Hmily implements TCC distributed transaction_transfer into and out of microservices to achieve the Cancel stage
Transfer to the microservice Cananl stage
/**
* 回滚
* @param userAccountDto
*/
@Transactional(rollbackFor = Exception.class)
public void cancelMethod(UserAccountDto userAccountDto){
String txNo = userAccountDto.getTxNo();
log.info("执行bank02的cancel方法,事务id: {}, 参数为:{}",txNo,JSONObject.toJSONString(userAccountDto));
CancelLog cancelLog = iCancelLogService.findByTxNo(txNo);
if(cancelLog != null){
log.info("bank02已经执行过Cancel方法,txNo:{}", txNo);
return;
}
// 保存记录
iCancelLogService.saveCancelLog(txNo);
userAccountMapper.cancelUserAccountBalanceBank02(userAccountDto.getAmount(),
userAccountDto.getTargetAccountNo());
}
Transfer out of the Microservice Cancel phase
/**
* 取消阶段
* @param userAccountDTO
*/
public void sayCancel(UserAccountDTO userAccountDTO) {
String txNo = userAccountDTO.getTxNo();
log.info("********** 执行bank01 的 Cancel方法 ,事务id={}",txNo);
// 1. 幂等处理
CancelLog cancelLog = cancelLogMapper.selectById(txNo);
if (cancelLog != null ){
return;
}
// 2、根据账户id查询账户
UserAccount userAccount = baseMapper.selectById(userAccountDTO.getSourceAccountNo());
userAccount.setAccountBalance(userAccount.getAccountBalance().add(userAccountDTO.getBigDecimal()));
userAccount.setTransferAmount(userAccount.getTransferAmount().subtract(userAccountDTO.getBigDecimal()));
baseMapper.updateById(userAccount);
// 3、记录回滚日志
CancelLog cancelLog1 = new CancelLog();
cancelLog1.setTxNo(txNo);
cancelLog1.setCreateTime(LocalDateTime.now());
cancelLogMapper.insert(cancelLog1);
}
Final Consistency Distributed Transaction Solution_What is Reliable Message Final Consistency Transaction
The basic principle of reliable message eventual consistency is that the transaction initiator (message sender) sends a message after successfully executing a local transaction, and the transaction participant (message consumer) receives the message sent by the transaction initiator and successfully executes the local transaction. The final data of the transaction initiator and the transaction participants can reach a consistent state.
Two implementations:
1. Based on the local message table
2. Based on message middleware that supports distributed transactions, such as RocketMQ, etc.
Fundamental
When using the reliable message final consistency scheme to solve the problem of distributed transactions, it is necessary to ensure the consistency of message sending and message consumption, so as to ensure the reliability of messages.
Realization of Reliable Message Final Consistency Distributed Transaction_Local Message Table
The core of the local message table mode ensures the consistency of data business operations and messages through local transactions, and then sends them to consumers through scheduled tasks or adds a layer of MQ in the middle to ensure the final consistency of data.
Library table design
Outbound local message table in the order microservice:
basic function
analyze