四TCC-transaction分布式事务关键组件--3事务存储器、TCC-ClientServer

四 TCC-transaction分布式事务关键组件--3事务存储器、TCC-Client/Server

2.2.4 事务存储器

在 TCC 的过程中,根据应用内存中的事务信息完成整个事务流程。But 实际业务场景中,将事务信息只放在应用内存中是远远不够可靠的。可能会出现如下的问题:

1 应用进程异常崩溃,未完成的事务信息将丢失。

2 应用进程集群,当提供远程服务调用时,事务信息需要集群内共享。

3 发起事务的应用需要重启部署新版本,因为各种原因,有未完成的事务

因此,TCC-Transaction除了将事务信息添加到内存中的同时,还提供外部存储来持久化事务对象。

image-20230518173457393

TCC提供了TransactionRepository来实现对事务对象Transaction的持久化。

但是,再前面分析Transaction时可以看到,事务对象本身是个复杂的对象,其内持有List等对象,内嵌较多的内容,因此在持久化时,需要对复杂的java对象,进行序列化处理,然后才可以持久化存储。

1 序列化

public interface ObjectSerializer<T> {
/**
* Serialize the given object to binary data.
*
* @param t object to serialize
* @return the equivalent binary data
*/
byte[] serialize(T t);
/**
* Deserialize an object from the given binary data.
*
* @param bytes object binary representation
* @return the equivalent object instance
*/
T deserialize(byte[] bytes);
T clone(T object);
}

image-20230518173926534

主要提供了JDK和Kryo的序列化解决方案,这里不是重点,省略分析过程。

2 TransactionRepository

image-20230518174037122

public interface TransactionRepository extends Closeable {
String getDomain()
int create(Transaction transaction); //存储
int update(Transaction transaction)//更新
int delete(Transaction transaction);//删除
Transaction findByXid(Xid xid);//查找
boolean supportRecovery();
//找到未修改的(事务恢复时候使用)
Page<Transaction> findAllUnmodifiedSince(Date date, String offset, int pageSize);
}

然后,事务存储的实现类:

这里只分析create储存事务的方法。

public class DefaultTransactionRepository implements TransactionRepository {
private String domain;
//事务store存储
private TransactionStorage transactionStorage;
//事务序列化器
private TransactionSerializer serializer;
@Override
public int create(Transaction transaction) {
transaction.setVersion(1L); //乐观锁,版本为0,表示未持久化;版本为1,表示已经对transaction进行了持久化
//TAG1 getTransactionStore(transaction)获取TransactionStore
TransactionStore transactionStore = getTransactionStore(transaction);
//TAG2 this.transactionStorage.create(transactionStore)持久化
int result = this.transactionStorage.create(transactionStore);
return result;
}

注意,在持久化transaction时,需要修改版本号为1,表示已经持久化过;为0,表示尚未持久化。

TAG1 getTransactionStore(transaction)
public TransactionStore getTransactionStore(Transaction transaction) {
return TransactionConvertor.getTransactionStore(serializer, this.domain, transaction);
}
public final class TransactionConvertor {
public static TransactionStore getTransactionStore(TransactionSerializer serializer, String domain, Transaction transaction) {
TransactionStore transactionStore = new TransactionStore();
transactionStore.setXid(transaction.getXid());
transactionStore.setRootXid(transaction.getRootXid());
transactionStore.setRootDomain(transaction.getRootDomain());
//serializer.serialize(transaction)序列化事务对象,并将其设置为transactionStore的内容
transactionStore.setContent(serializer.serialize(transaction));
transactionStore.setStatusId(transaction.getStatus().getId());
transactionStore.setVersion(transaction.getVersion());
transactionStore.setLastUpdateTime(transaction.getLastUpdateTime());
transactionStore.setRetriedCount(transaction.getRetriedCount());
transactionStore.setCreateTime(transaction.getCreateTime());
transactionStore.setDomain(domain);
transactionStore.setTransactionTypeId(transaction.getTransactionType().getId());
return transactionStore;
}
}

通过TransactionSerializer序列化transaction,并初始化一个TransactionStore的事务存储对象。该对象作为一个封装的事务对象,投入TransactionStorage中进行事务的持久化。

TAG2 this.transactionStorage.create(transactionStore)持久化

3 TransactionStorage事务存储器

public interface TransactionStorage extends Closeable {
//增删改查
int create(TransactionStore transactionStore);
int update(TransactionStore transactionStore);
int delete(TransactionStore transactionStore);
TransactionStore findByXid(String domain, Xid xid);
TransactionStore findMarkDeletedByXid(String domain, Xid xid);
//标记待删除
int markDeleted(TransactionStore transactionStore);
int restore(TransactionStore transactionStore);
// 彻底删除标记过的事务
int completelyDelete(TransactionStore transactionStore);
boolean supportStorageRecoverable();
}

image-20230518175830910

这里仅仅以JdbcTransactionStorage进行分析

3.1 JdbcTransactionStorage.create
AbstractTransactionStorage
@Override
public int create(TransactionStore transactionStore) {
if (transactionStore.getContent().length > this.storeConfig.getMaxTransactionSize()) {
throw new TransactionIOException(String.format("cur transaction size(%dB) is bigger than maxTransactionSize(%dB), consider to reduce parameter size or adjust maxTransactionSize", transactionStore.getContent().length, this.storeConfig.getMaxTransactionSize()));
}
//调用子类的方法,持久化
int result = doCreate(transactionStore);
if (result > 0) {
return result;
} else {
TransactionStore foundTransactionStore = findByXid(transactionStore.getDomain(), transactionStore.getXid());
if (foundTransactionStore != null && transactionStore.getRequestId() != null
&& transactionStore.getRequestId().equals(foundTransactionStore.getRequestId())
&& transactionStore.getVersion() == foundTransactionStore.getVersion()) {
return 1;
}
throw new TransactionIOException(transactionStore.simpleDetail());
}
}
public class JdbcTransactionStorage extends AbstractTransactionStorage implements StorageRecoverable {
private static final int MARK_DELETED_YES = 1;
private static final int MARK_DELETED_NO = 0;
private static final String SQL_SELECT_PREFIX_FOR_TCC_TRANSACTION = "SELECT DOMAIN,ROOT_XID,XID,CONTENT,STATUS,TRANSACTION_TYPE,CREATE_TIME,LAST_UPDATE_TIME,RETRIED_COUNT,VERSION,IS_DELETE,ROOT_DOMAIN,REQUEST_ID FROM ";
private static final String SQL_SELECT_PREFIX_FOR_TCC_DOMAIN = "SELECT DOMAIN, PHONE_NUMBERS, ALERT_TYPE, THRESHOLD, INTERVAL_MINUTES,LAST_ALERT_TIME,DING_ROBOT_URL,CREATE_TIME,LAST_UPDATE_TIME,VERSION FROM ";
private String tbSuffix;
private DataSource dataSource;
public JdbcTransactionStorage(TransactionStoreSerializer serializer, StoreConfig storeConfig) {
super(serializer, storeConfig);
this.tbSuffix = storeConfig.getTbSuffix();
this.dataSource = storeConfig.getDataSource();
}
@Override
protected int doCreate(TransactionStore transactionStore) {
if (doFindOne(transactionStore.getDomain(), transactionStore.getXid(), false) != null) {
return 0;
}
Connection connection = null;
PreparedStatement stmt = null;
try {
connection = this.getConnection(); //获取connection数据库连接
//构建insert的sql语句
StringBuilder builder = new StringBuilder();
builder
.append("INSERT INTO ")
.append(getTableName())
.append("(ROOT_XID,XID,TRANSACTION_TYPE,CONTENT,STATUS,RETRIED_COUNT,CREATE_TIME,LAST_UPDATE_TIME,VERSION,ROOT_DOMAIN,DOMAIN,REQUEST_ID) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)");
//构建insert的sql语句
stmt = connection.prepareStatement(builder.toString());
stmt.setString(1, transactionStore.getRootXid().toString());
stmt.setString(2, transactionStore.getXid().toString());
stmt.setInt(3, transactionStore.getTransactionTypeId());
stmt.setBytes(4, transactionStore.getContent());
stmt.setInt(5, transactionStore.getStatusId());
stmt.setInt(6, transactionStore.getRetriedCount());
stmt.setTimestamp(7, new Timestamp(transactionStore.getCreateTime().getTime()));
stmt.setTimestamp(8, new Timestamp(transactionStore.getLastUpdateTime().getTime()));
stmt.setLong(9, transactionStore.getVersion());
stmt.setString(10, transactionStore.getRootDomain());
stmt.setString(11, transactionStore.getDomain());
if (transactionStore.getRequestId() != null) {
stmt.setInt(12, transactionStore.getRequestId());
} else {
stmt.setNull(12, Types.INTEGER);
}
return stmt.executeUpdate(); //执行sql插入insert语句
} catch (SQLException e) {
throw new TransactionIOException(e);
} finally {
closeStatement(stmt);
this.releaseConnection(connection);
}
}

TCC-Transaction表的数据结构如下:

CREATE TABLE `TCC_TRANSACTION` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2.2.5 事务恢复

事务信息被持久化到外部的存储器中。事务存储是事务恢复的基础。通过读取外部存储器中的异常事务,定时任务会按照一定频率对事务进行重试,直到事务完成或超过最大重试次数。

详细内容见 五 TCC-transaction的事务恢复

image-20230519160125616

2.2.6 TCC-Client/Tcc-Server

image-20230612174242523

对于SpringTccClient作为tcc-client,是在微服务端配置。

public class SpringTccClient extends TccClient implements TransactionManagerFactory {
public SpringTccClient() {
super(new ClientConfig());
}
public SpringTccClient(ClientConfig clientConfig) {
super(clientConfig);
}
}

当前类在spring启动时,作为tcc-client,被加载:

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan(value = "org.mengyun.tcctransaction", excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {XmlTccTransactionConfiguration.class})})
public class AnnotationTccTransactionConfiguration {
@Bean
@DependsOn({"springBeanFactory"})
public SpringTccClient getTccClient() {
return new SpringTccClient(clientConfig);
}
}

而tcc-server类,由于作为服务端单独部署,因此不是spring启动时加载。

posted @   LeasonXue  阅读(78)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示