四TCC-transaction分布式事务关键组件--3事务存储器、TCC-ClientServer
四 TCC-transaction分布式事务关键组件--3事务存储器、TCC-Client/Server
2.2.4 事务存储器
在 TCC 的过程中,根据应用内存中的事务信息完成整个事务流程。But 实际业务场景中,将事务信息只放在应用内存中是远远不够可靠的。可能会出现如下的问题:
1 应用进程异常崩溃,未完成的事务信息将丢失。
2 应用进程集群,当提供远程服务调用时,事务信息需要集群内共享。
3 发起事务的应用需要重启部署新版本,因为各种原因,有未完成的事务
因此,TCC-Transaction除了将事务信息添加到内存中的同时,还提供外部存储来持久化事务对象。
TCC提供了TransactionRepository来实现对事务对象Transaction的持久化。
但是,再前面分析Transaction时可以看到,事务对象本身是个复杂的对象,其内持有List
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); }
主要提供了JDK和Kryo的序列化解决方案,这里不是重点,省略分析过程。
2 TransactionRepository
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(); }
这里仅仅以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的事务恢复
2.2.6 TCC-Client/Tcc-Server
对于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启动时加载。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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语句:使用策略模式优化代码结构