Seata数据源代理:分布式事务的"物流监控系统"
引言:快递公司的智能升级
想象一家普通快递公司突然获得超能力:每个包裹都自带监控摄像头,仓库配备智能分拣系统,快递员配备轨迹记录仪。这正是Seata数据源代理要完成的工作——让普通数据库连接获得"事务监控超能力"。让我们拆解这套"物流监控系统"的技术实现。
一、数据源代理类:快递公司的安检门
1.1 设计模式:装饰器模式
DataSourceProxy如同快递站入口的智能安检门,对原始数据源(普通快递车)进行增强:
public class DataSourceProxy extends AbstractDataSourceProxy {
private final DataSource targetDataSource; // 被装饰的原始数据源
public ConnectionProxy getConnection() throws SQLException {
// 创建具备监控能力的连接
return new ConnectionProxy(targetDataSource.getConnection(),
resourceManager, defaultAutoCommit);
}
}
就像给每辆快递车安装GPS定位器,在保持原有运输能力的基础上增加轨迹追踪功能。
1.2 核心能力
- 连接包装:将原生Connection升级为ConnectionProxy
- 资源注册:自动向TransactionManager注册分支事务
- SQL解析:识别业务SQL中的"危险品"(更新操作)
二、资源管理器:物流调度中心
2.1 ResourceManager设计
public interface ResourceManager {
void registerResource(Resource resource); // 注册分支事务资源
void commit(String xid, long branchId); // 提交指令下发
void rollback(String xid, long branchId); // 回滚指令处理
}
这就像物流调度中心的控制台,管理着所有分支仓库(数据库资源)的状态。
2.2 分支事务管理
当新的快递分站(数据库连接)加入时:
// DefaultResourceManager.java
public void registerResource(Resource resource) {
if (resource instanceof DataSourceProxy) {
dataSourceManager.registerResource((DataSourceProxy)resource);
}
// 其他资源类型处理...
}
每个分站都会被记录在调度中心的登记簿中,确保异常时能快速定位问题包裹。
三、数据库连接代理:快递员的智能手环
3.1 ConnectionProxy结构
public class ConnectionProxy implements Connection {
private final Connection originalConnection; // 原生连接
private final DataSourceProxy dataSourceProxy;
public void commit() throws SQLException {
if (isInGlobalTransaction()) { // 全局事务中
reportToTransactionManager(); // 向调度中心汇报
} else {
originalConnection.commit(); // 本地直接提交
}
}
}
就像快递员的手环,在普通配送(本地事务)时正常运作,在跨城运输(全局事务)时自动开启轨迹记录。
3.2 自动提交管理
public void setAutoCommit(boolean autoCommit) {
if (autoCommit && isInGlobalTransaction()) {
// 全局事务中禁止自动提交
throw new SQLException("Seata禁止在全局事务中启用自动提交");
}
originalConnection.setAutoCommit(autoCommit);
}
这相当于强制要求跨城运输必须使用指定路线,避免快递员私自改变配送路线。
四、Statement代理:包裹面单扫描仪
4.1 StatementProxy拦截机制
public class StatementProxy extends AbstractStatementProxy
implements Statement {
public ResultSet executeQuery(String sql) throws SQLException {
// 1. SQL解析(面单信息识别)
Executor executor = ExecutorFactory.getExecutor(sql);
// 2. 前置处理(拍摄发货前照片)
TableRecords beforeImage = executor.beforeImage();
// 3. 执行原始操作(实际运输)
ResultSet rs = originalStatement.executeQuery(sql);
// 4. 后置处理(拍摄发货后照片)
TableRecords afterImage = executor.afterImage(beforeImage);
// 5. 保存快照到undo_log(面单存档)
UndoLogManager.saveUndoLog(...);
return rs;
}
}
这个过程就像智能面单扫描仪,在包裹出入库时自动拍摄对比照片,确保异常时可追溯。
4.2 PreparedStatementProxy增强
public class PreparedStatementProxy extends AbstractPreparedStatementProxy {
public int executeUpdate() throws SQLException {
// 参数化SQL解析(识别危险品类型)
SQLRecognizer recognizer = SQLVisitorFactory.get(sql);
// 生成唯一包裹ID(行锁键)
List<String> lockKeys = LockKeyBuilder.buildLockKey(recognizer, parameters);
// 向TC申请锁(运输许可证)
boolean lockResult = DataSourceManager.lock(lockKeys);
// 后续处理与StatementProxy类似...
}
}
这种设计就像对特殊包裹(参数化SQL)进行深度安检,确保不会与其他运输任务冲突。
五、技术全景:物流监控体系
graph LR
A[应用代码] --> B(DataSourceProxy)
B --> C[ConnectionProxy]
C --> D[StatementProxy]
C --> E[PreparedStatementProxy]
D --> F{SQL执行拦截}
E --> F
F --> G[UndoLog记录]
F --> H[全局锁管理]
G --> I[事务回滚]
H --> J[数据一致性]
classDef tech fill:#f9f,stroke:#333;
class B,C,D,E tech;
六、设计启示:监控与透明的平衡
- 代理模式的精准应用:如同给快递车加装监控但不影响运输能力
- 责任链模式的灵活运用:每个环节只处理特定类型"包裹"(SQL类型)
- 模板方法模式的巧妙实现:在Statement执行流程中固化监控逻辑
源码启示:Seata 1.5.2版本中,
executor模块的AbstractDMLBaseExecutor定义了SQL执行的标准流程,如同物流公司制定的标准操作手册。
结语:透明化改造的艺术
Seata的数据源代理体系,就像给传统物流系统植入神经感知网络。通过层层代理的"传感器"(Proxy对象),在不改变原有运输流程(业务代码)的前提下,实现了全链路监控。这种设计哲学告诉我们:优秀的中间件应该像空气一样——感受不到存在,但时刻提供保护。