我正在使用akka流来处理我的数据。在其中有1个Source由元素UUID组成。
流程如下:
现在,我想向此流添加重试机制,以便如果流中的任何阶段失败,则应重试该阶段一段时间(例如3),如果之后失败,则流的唯一失败应该发生。例如,如果第三方服务存在一些问题,例如HTTP 504错误,则大多数情况下重试此元素成功之后。那么akka中有什么方法可以做到这一点。
目前,我正在维护1个列表,以存储所有失败的元素ID,如下所示。
代码:
List<UUID> failedId = new CopyOnWriteArrayList<>();
Source.from(elementIdToProcess).map(f -> {
failedId.add(f);
return f;
}).via(featureFlow()).via(filterNullFeaturesFlow())
.via(mapToFeatureFlow()).via(setFeaturesAdditionalInfo())
.runForeach(features -> {
features.parallelStream().forEach(feature -> {
try {
featureCommitter.save(feature);
failedId.remove(UUID.fromString(feature.getFeatureId()));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}, materializer);
答案 0 :(得分:1)
我遇到了类似的问题,并使用名为retry的未来功能解决了该问题。
根据您的代码,我可以这样实现:
@BeforeClass
public static void setUpClass() {
system = ActorSystem.create();
mat = ActorMaterializer.create(system);
}
@AfterClass
public static void tearDown() {
TestKit.shutdownActorSystem(system);
system = null;
}
@Test
public void shouldRetryArbitraryNumberOfTimes() {
doThrow(new RuntimeException()).when(featureCommitter).process(anyString(), anyString(), anyString());
TestSubscriber.Probe<Message> probe = getJmsSource().runWith(TestSink.probe(system), mat);
Throwable throwable = probe.request(1).expectError();
verify(featureCommitter, timeout(5000).times(4)).save(any(Feature.class));
}
private Source<Feature> getJmsSource() {
List<UUID> failedId = new CopyOnWriteArrayList<>();
return Source.from(elementIdToProcess)
.via(featureFlow())
.via(filterNullFeaturesFlow())
.via(mapToFeatureFlow())
.via(setFeaturesAdditionalInfo())
.mapAsync(1, features -> {
features.parallelStream().forEach(feature -> retry(getCompletionStageCallable(feature),
3,
Duration.ofMillis(200),
system.scheduler(),
system.dispatcher());
});
}
private Callable<CompletionStage<Feature>> getCompletionStageCallable(Feature feature) {
() -> {
return return CompletableFuture.supplyAsync(() -> {
try {
featureCommitter.save(feature);
} catch (Exception e) {
throw new RuntimeException(e);
}
return feature;
}
}
这里要考虑的主要事情是,我们不是将重试作为流的一部分来处理,而是作为处理未来的一种方式。
如您所见,我将保存移出接收器,并移到了mapAsync
中,在其中我将并行度设置为1。这样,我就可以使用TestSubscriber.Probe
来验证流的行为。>
我希望这会有所帮助。
致谢
答案 1 :(得分:0)
您可以这样重试
source.recoverWithRetries(attempts = 2, {
case _: RuntimeException ⇒ source
})
或者您可以使用RestartSource
,RestartSink
或RestartFlow
采取退避策略。
所有这些都记录在Akka docs