Java应用程序:序列工作流模式

Java应用程序:序列工作流模式,java,spring-batch,workflow,microservices,project-reactor,Java,Spring Batch,Workflow,Microservices,Project Reactor,我有一个SpringWeb应用程序。当用户调用saveendpoint时,系统应该执行许多外部调用以在多个微服务中保存状态。然而,这些步骤相互依赖。换句话说,我有一系列的步骤要执行 一个接一个地调用一组步骤并不是什么大问题,我可以为每个步骤创建类,并在步骤之间进行适当的修改,逐个调用它们 但是,每个步骤都可能失败,如果发生,应向用户正确报告。下面是一个用于直接解决方案的伪代码: var response = new Response() try { var result1 = step1

我有一个SpringWeb应用程序。当用户调用saveendpoint时,系统应该执行许多外部调用以在多个微服务中保存状态。然而,这些步骤相互依赖。换句话说,我有一系列的步骤要执行

一个接一个地调用一组步骤并不是什么大问题,我可以为每个步骤创建类,并在步骤之间进行适当的修改,逐个调用它们

但是,每个步骤都可能失败,如果发生,应向用户正确报告。下面是一个用于直接解决方案的伪代码:

var response = new Response()
try {
    var result1 = step1.execute(args1)
    var args2 = process(result1, args1)
    var result2 = step2.execute(args2)
    ...
catch(Step1Exception e) {
    response.setIsPartialSuccess(true);
    response.setPartialResults(e.getDetails())
}
catch(Step2Exception e) {
    response.setIsPartialSuccess(true);
    response.setPartialResults(e.getDetails())
}
return response; 
每个步骤都可以处理项目列表。有些步骤会一次发送所有项目(要么全部失败,要么没有),有些步骤会一个接一个地发送(一半可以失败,一半可以通过)。StepException将包含该信息,即通过的内容和失败的内容

正如您所看到的,它不是真正可维护的。在这里使用springbatch会有点过分,因为我没有读写东西,我不需要任何多线程、作业细节或检查点。然而,这个想法非常相似,我想创建一些构建块并控制流

目前,我正试图弄清楚SpringReactor是否可以在这里提供帮助(是的,我知道它有不同的用途),因为它有一些错误处理的流/管道。想象一下,我可以写这样的东西:

var context = new Context(response, args1);
Mono.just(context)
    .map(step1::execute)
    .onErrorReturn(e -> context.withError(e))
    //I assume if error happened before
    //steps below are not executed
    .map(step2::execute) 
    .onErrorReturn(e -> context.withError(e))
    .block()
 return context;
您可以将被动应用程序处理的数据视为在装配线中移动。反应器既是传送带又是工作站。原材料从一个来源(原始出版商)倾泻而出,最终成为成品,准备推送到消费者(或订阅者)手中

原材料可以经过各种转换和其他中间步骤,也可以成为将中间件聚集在一起的大型装配线的一部分。如果某一点出现故障或堵塞(可能装箱产品需要的时间过长),受影响的工作站可以向上游发出信号,以限制原材料的流动


换句话说,我正在寻找一个类似于上述的框架。我现在不需要任何异步处理或重试,但它们将来可能会有用。请告诉我是否有比reactor更好的方法来满足我的需要。

即使您现在不需要非阻塞异步调用,reactor仍然非常适合于此,因为它擅长协调这种处理管道。我认为Java8
Stream
也可以满足要求,但在这方面功能稍逊

为了清晰起见,我扩展了方法引用,加上我的一些猜测,您的代码在Reactor中看起来像这样:

var response = Mono.just(initialArgs)
    .flatMap(args1 -> Mono.fromCallable(() -> step1.execute(args1))
        .map(result1 -> process(result1, args1) //args1 still in scope inside flatMap
    )
    .flatMap(args2 -> Mono.fromCallable(() -> step2.execute(args2))
    //alternatively to last flatMap, with caveat:
    //.map(args2 -> step2.execute(args2))
    .map(endResult -> new Response(endResult))
    .onErrorResume(error -> {
        Response errorResponse = new Response();
        errorResponse.setIsPartialSuccess(true);
        errorResponse.setPartialResults(error.getDetails());
        return Mono.just(errorResponse);
    })
    .block();
此特定链中使用的运算符不会更改线程,因此这将在调用最后一个
block()
方法的线程中执行

任何步骤中的错误都会停止整个处理并传播到最后(
block()
将引发异常)


请注意,一些操作符(主要是那些有时间概念的操作符)会更改线程,此时
stepX.execute
被阻塞会成为一个问题,因为这会阻塞应该由整个反应器代码(而不仅仅是特定的处理管道)共享的线程而且资源有限。

在没有答案的情况下发现了一个类似的问题: