我有一个Spring Web应用程序。当用户调用保存端点时,系统应执行许多外部调用以将状态保存到多个微服务中。但是,这些步骤相互依赖。换句话说,我要执行一系列步骤。 sequence pattern
仅一步步调用就没什么大不了的,我可以为每个步骤创建一个类,然后在步骤之间进行适当的修改就逐个调用它们。
但是,每个步骤都可能失败,如果发生,则应将其正确报告给用户。这是简单解决方案的伪代码:
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将包含该信息,即传递的信息,失败的信息。
如您所见,它并不是真正可维护的。在这里使用Spring Batch可能会过大,因为我不需要读写东西,所以不需要任何多线程,工作细节或检查点。但是,这个想法非常相似,我想创建一些构建基块并控制流程。
此刻,我正在尝试弄清Spring Reactor是否可以在这里提供帮助(是的,我知道这是出于不同的目的),因为它具有流/管道并带有一些错误处理。想象一下我可以写类似的东西:
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;
您可以将反应式应用程序处理的数据视为流水线。反应器既是传送带又是工作站。原材料从来源(原始发布者)倒出,最终成为准备好推向消费者(或订阅者)的成品。
原材料可以经过各种转换和其他中间步骤,也可以成为将中间零件聚集在一起的较大装配线的一部分。如果某一点出现故障或堵塞(也许装箱的产品花费的时间过长),那么受灾的工作站可以向上游发出信号,以限制原材料的流量。
换句话说,我正在寻找一个类似于上面的框架。我现在不需要任何异步处理或重试,但是它们将来可能会有用。请让我知道是否有比我的需求更好的反应堆。
答案 0 :(得分:2)
即使您现在不需要非阻塞的异步调用,Reactor仍然可以很好地解决此问题,因为它擅长编排这种处理管道。我认为Java 8 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
被阻塞成为一个问题,因为这将阻塞应该由整个Reactor共享的线程代码(不仅是特定的处理管道)而且资源有限。