题目
设计支持事务回滚的资源管理器
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
自定义异常设计,资源安全释放,异常传播控制,补偿操作实现,异常抑制机制
快速回答
实现要点:
- 定义
TransactionException封装原始异常和补偿异常 - 使用
LinkedHashMap存储资源及补偿操作 execute()方法中:- 顺序执行操作,失败时触发回滚
- 逆序执行补偿操作
- 使用
addSuppressed()记录补偿异常
- 用
try-with-resources确保资源关闭 - 补偿异常不中断回滚流程
问题场景
在分布式系统中管理多个资源(如数据库、消息队列)时,需要实现原子操作:所有资源操作成功则提交,任一失败则执行补偿操作回滚。难点在于:
- 资源类型异构(JDBC连接、文件流、API客户端)
- 补偿操作可能失败
- 需要保留原始异常上下文
核心实现
1. 自定义异常类
public class TransactionException extends Exception {
public TransactionException(String message, Throwable cause) {
super(message, cause);
}
}2. 资源管理器实现
public class ResourceManager implements AutoCloseable {
private final LinkedHashMap<AutoCloseable, Runnable> resources = new LinkedHashMap<>();
public void addResource(AutoCloseable resource, Runnable compensation) {
resources.put(resource, compensation);
}
public void execute() throws TransactionException {
List<AutoCloseable> succeeded = new ArrayList<>();
TransactionException firstException = null;
// 顺序执行操作
for (Map.Entry<AutoCloseable, Runnable> entry : resources.entrySet()) {
try {
if (entry.getKey() instanceof DatabaseConnection) {
((DatabaseConnection) entry.getKey()).executeUpdate();
} else if (entry.getKey() instanceof FileHandler) {
((FileHandler) entry.getKey()).write();
}
succeeded.add(entry.getKey());
} catch (Exception e) {
firstException = new TransactionException("Operation failed", e);
break;
}
}
// 失败时执行补偿
if (firstException != null) {
for (int i = succeeded.size() - 1; i >= 0; i--) {
try {
resources.get(succeeded.get(i)).run();
} catch (Exception compEx) {
firstException.addSuppressed(compEx);
}
}
throw firstException;
}
}
@Override
public void close() {
resources.keySet().forEach(res -> {
try { res.close(); } catch (Exception ignored) {}
});
}
}关键设计原理
1. 异常传播控制
- 操作失败时立即捕获异常,但不立即抛出,先记录第一个异常
- 补偿操作中的异常通过
addSuppressed()附加到主异常
2. 资源安全释放
- 实现
AutoCloseable接口,支持try-with-resources close()方法独立于事务结果,始终执行
3. 补偿机制
- 使用
LinkedHashMap保证资源添加顺序 - 回滚时逆序执行补偿(LIFO原则)
- 补偿操作封装为
Runnable实现解耦
最佳实践
- 异常抑制:Java 7+的
addSuppressed()避免异常信息丢失 - 资源隔离:操作异常与关闭逻辑分离
- 防御性编程:补偿操作捕获所有异常,确保继续执行后续补偿
- 日志记录:关键节点记录操作状态,便于故障排查
常见错误
| 错误类型 | 后果 | 解决方案 |
|---|---|---|
| 直接抛出补偿异常 | 掩盖原始操作异常 | 使用addSuppressed()附加异常 |
| 未逆序执行补偿 | 资源依赖导致状态不一致 | 严格按LIFO顺序回滚 |
| 忽略资源关闭 | 资源泄漏 | 实现AutoCloseable接口 |
| 补偿操作中断 | 部分资源未回滚 | 捕获补偿中的所有异常 |
扩展知识
- Saga模式:长事务解决方案,每个操作对应补偿操作
- Try-With-Resources原理:编译器自动生成finally块调用close()
- 异常消耗性能:创建异常栈跟踪代价高,高频场景需优化
- Spring的TransactionTemplate:声明式事务管理的底层实现参考