java - 方法内部 'rollback' REST 方法调用的最佳实践

标签 java rest spring-boot rollback undo

标题可能不正确,但我会尽力解释我的问题。我的项目是一个 Spring Boot 项目。我有调用外部 REST 端点的服务。

我有一个服务方法,其中包含对我拥有的其他服务的多个方法调用。每个单独的方法调用都可以成功或不成功。每个方法调用都是对 REST 端点完成的,并且可能会出现问题,例如 web 服务不可用或在极少数情况下会抛出未知异常。无论发生什么,我都需要能够跟踪哪些方法调用成功,如果其中任何一个失败,我想回滚到原始状态,就好像什么都没发生一样,将其视为 @Transactional 注释。所有 REST 调用都是不同的端点,需要单独调用,并且来 self 没有影响的外部方。示例:

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 应用程序内部。这对我的用例来说是否也正确,或者如果它成功,我应该跟踪每个方法/web 服务调用吗?是否有这样做的最佳实践?

我想无论我如何跟踪它,我都需要知道哪个方法调用失败并从那里执行我的“撤消”方法 REST 调用。尽管理论上这些调用当然也可能会失败。

我的主要目标是不创建尚未执行任何进一步处理的文件(在我的示例中)。它要么全部成功,要么一无所获。一种交易。

更新 1:改进了基于评论的伪实现:

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
      }
}

最佳答案

你的操作看起来像一个事务,所以你可以使用@Transactional注解。从您的代码中,我无法真正了解您是如何管理每个操作的 HTTP 响应调用的,但是您应该考虑让您的服务方法返回它们,然后根据响应调用进行回滚。您可以像这样创建一组方法,但您希望自己的逻辑具体如何取决于您。

private Process[] restCalls = new Process[] {
        new Process() { public void call() { process1Service.createFileRESTcall(); } },
        new Process() { public void call() { process2Service.addFilePermissionsRESTcall(); } },
        new Process() { public void call() { process3Service.addFileMetadataRESTcall(); } },
        new Process() { public void call() { process4Service.addFileTimestampRESTcall(); } },
};

interface Process {
    void call();
}

@Transactional(rollbackOn = Exception.class)
public void bundledProcess() {
    restCalls[0].call(); 
    ... // say, see which process returned wrong response code
}

@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollback() {
    // handle rollback according to failed method index
}

检查 this article.可能会派上用场。

关于java - 方法内部 'rollback' REST 方法调用的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49225818/

相关文章:

java - 无法使用 Spring 和 JavaConfig 创建服务

java - REST 身份验证 : CDI safe? CDI @Produces 的哪个范围?

web-services - SOAP over SMTP/JMS 的任何用例?

java - System.currentTimeMillis();如果我更改系统时间

Java - 如何同步 1 个列表上的 2 个线程?

java - Tomcat 7 和 Spring boot war 启动

java - Spring Boot中的MessageSource是否允许使用同一语言的多个属性文件?

spring-boot - @WebMvcTest 需要模拟与测试无关的 bean

java - Sybase ASE 到 HSQLDB JUnit java.sql.SQLSyntaxErrorException : type not found or user lacks privilege

java - android中同时调用多个rest服务链接