【发布时间】:2018-08-19 22:01:20
【问题描述】:
标题可能不正确,但我会尽力解释我的问题。我的项目是一个 Spring Boot 项目。我有调用外部 REST 端点的服务。
我有一个服务方法,其中包含对我拥有的其他服务的多个方法调用。每个单独的方法调用都可能成功或失败。每个方法调用都是针对 REST 端点进行的,并且可能会出现诸如 Web 服务不可用或在极少数情况下引发未知异常等问题。无论发生什么,我需要能够跟踪哪些方法调用是成功的,如果其中任何一个调用失败,我想回滚到原始状态,就好像什么都没发生一样,将其视为@Transactional 注释。所有 REST 调用都是不同的端点,需要单独调用,并且来自我没有影响的外部方。示例:
public MyServiceImpl implements MyService {
@Autowired
private Process1Service;
@Autowired
private Process2Service;
@Autowired
private Process3Service;
@Autowired
private Process4Service;
public void bundledProcess() {
process1Service.createFileRESTcall();
process2Service.addFilePermissionsRESTcall();
process3Service.addFileMetadataRESTcall(); <-- might fail for example
process4Service.addFileTimestampRESTcall();
}
}
如果例如 process3Service.addFileMetadataRESTcall 失败,我想为 process3 之前的每个步骤执行类似撤消(以相反顺序)的操作:
process2Service.removeFilePermissionsRESTcall();
process1Service.deleteFileRESTcall();
我阅读了有关命令模式的信息,但它似乎用于应用程序内的撤消操作,作为执行操作的一种历史记录,而不是在 Spring Web 应用程序内。这对我的用例是否也正确,或者如果成功,我应该跟踪每个方法/网络服务调用吗?是否有执行此操作的最佳做法?
我想无论我如何跟踪它,我都需要知道哪个方法调用失败并从那里执行我的“撤消”方法 REST 调用。虽然理论上即使这些调用当然也可能失败。
我的主要目标是不创建未执行任何进一步处理的文件(在我的示例中)。它应该要么全部成功,要么一无所获。一种事务性的。
Update1:基于 cmets 改进的伪实现:
public Process1ServiceImpl implements Process1Service {
public void createFileRESTcall() throws MyException {
// Call an external REST api, pseudo code:
if (REST-call fails) {
throw new MyException("External REST api failed");
}
}
}
public class BundledProcessEvent {
private boolean createFileSuccess;
private boolean addFilePermissionsSuccess;
private boolean addFileMetadataSuccess;
private boolean addFileTimestampSuccess;
// Getters and setters
}
public MyServiceImpl implements MyService {
@Autowired
private Process1Service;
@Autowired
private Process2Service;
@Autowired
private Process3Service;
@Autowired
private Process4Service;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Transactional(rollbackOn = MyException.class)
public void bundledProcess() {
BundleProcessEvent bundleProcessEvent = new BundleProcessEvent();
this.applicationEventPublisher.publishEvent(bundleProcessEvent);
bundleProcessEvent.setCreateFileSuccess = bundprocess1Service.createFileRESTcall();
bundleProcessEvent.setAddFilePermissionsSuccess = process2Service.addFilePermissionsRESTcall();
bundleProcessEvent.setAddFileMetadataSuccess = process3Service.addFileMetadataRESTcall();
bundleProcessEvent.setAddFileTimestampSuccess = process4Service.addFileTimestampRESTcall();
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollback(BundleProcessEvent bundleProcessEvent) {
// If the last process event is successful, we should not
// be in this rollback method even
//if (bundleProcessEvent.isAddFileTimestampSuccess()) {
// remove timestamp
//}
if (bundleProcessEvent.isAddFileMetadataSuccess()) {
// remove metadata
}
if (bundleProcessEvent.isAddFilePermissionsSuccess()) {
// remove file permissions
}
if (bundleProcessEvent.isCreateFileSuccess()) {
// remove file
}
}
【问题讨论】:
-
除了以下答案之外,如果可能并且下游系统允许,您可以查看补偿交易。这基本上是在进行与原始交易完全相反的另一笔交易。
标签: java rest spring-boot rollback undo