如何并行使用多个消息并检测所有执行何时完成?

时间:2016-08-05 15:16:39

标签: java apache-camel

我想发送多条消息,这些消息将异步遍历同一条路径,并能够知道所有处理何时完成。

由于我需要知道每条路线何时终止,我想到了使用 ProducerTemplate#asyncRequestBody使用 InOut 模式,以便在返回的Future对象上调用get将阻塞,直到路由终止。

到目前为止一切顺利,每个请求都异步发送到路由,循环遍历所有Future调用get方法将阻塞所有请求 我的路线已经完成。

问题在于,虽然请求是异步发送的,但我希望它们也可以并行使用。

例如,假设P是ProducerTemplate,R n beeing请求,E n 是端点 - 我想要的是:

  ->   R0 -> from(E1).to(E2).to(E3) : done.
 /
P ->   R1 -> from(E1).to(E2).to(E3) : done.
 \  
  ->   R2 -> from(E1).to(E2).to(E3) : done.

        ^__ Requests consumed in parallel.

经过一些研究后,我偶然发现Competing Consumers并行执行,增加了更多的消费者。

但是,由于同时存在多个执行,这会减慢每条路由的执行速度,导致某些ExchangeTimedOutException

The OUT message was not received within: 20000 millis due reply message with correlationID...

因为我发送 InOut 请求,所以并不奇怪。但实际上,我并不真正关心这种反应,我只是用它来知道 当我的路线终止时我会使用 InOnly ProducerTemplate#asyncSendBody),但调用Future#get不会阻止,直到 整个任务完成。

是否有另一种替代方法可以异步发送请求并检测它们何时完成?

请注意,在我的情况下,更改超时不是一个选项。

2 个答案:

答案 0 :(得分:1)

我的第一直觉是建议使用NotifyBuilder来跟踪处理,更具体地说,使用whenBodiesDone来定位特定的身体。

修改

这是一个简单的实现,但确实证明了一点:

@SpringBootApplication
public class DemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

  @Component
  public static class ParallelProcessingRouteBuilder extends RouteBuilder {
    @Override
    public void configure() throws Exception {
      from("seda:test?concurrentConsumers=5")
          .routeId("parallel")
          .log("Received ${body}, processing")
          .delay(5000)
          .log("Processed ${body}")
          .stop();

      from("timer:testStarter?delay=3000&period=300000")
          .routeId("test timer")
          .process(exchange -> {
            // messages we want to track
            List<Integer> toSend = IntStream.range(0, 5).boxed().collect(toList());

            NotifyBuilder builder = new NotifyBuilder(getContext())
                .fromRoute("parallel")
                .filter(e -> toSend.contains(e.getIn().getBody(Integer.class)))
                .whenDone(toSend.size())
                .create();

            ProducerTemplate template = getContext().createProducerTemplate();

            // messages we do not want to track
            IntStream.range(10, 15)
                .forEach(body -> template.sendBody("seda:test", body)); 

            toSend.forEach(body -> template.sendBody("seda:test", body)); 

            exchange.getIn().setBody(builder.matches(1, TimeUnit.MINUTES));
          })
          .log("Matched? ${body}");
    }
  }
}

以下是日志示例:

2016-08-06 11:45:03.861  INFO 27410 --- [1 - seda://test] parallel                                 : Received 10, processing
2016-08-06 11:45:03.861  INFO 27410 --- [5 - seda://test] parallel                                 : Received 11, processing
2016-08-06 11:45:03.864  INFO 27410 --- [2 - seda://test] parallel                                 : Received 12, processing
2016-08-06 11:45:03.865  INFO 27410 --- [4 - seda://test] parallel                                 : Received 13, processing
2016-08-06 11:45:03.866  INFO 27410 --- [3 - seda://test] parallel                                 : Received 14, processing
2016-08-06 11:45:08.867  INFO 27410 --- [1 - seda://test] parallel                                 : Processed 10
2016-08-06 11:45:08.867  INFO 27410 --- [3 - seda://test] parallel                                 : Processed 14
2016-08-06 11:45:08.867  INFO 27410 --- [4 - seda://test] parallel                                 : Processed 13
2016-08-06 11:45:08.868  INFO 27410 --- [2 - seda://test] parallel                                 : Processed 12
2016-08-06 11:45:08.868  INFO 27410 --- [5 - seda://test] parallel                                 : Processed 11
2016-08-06 11:45:08.870  INFO 27410 --- [1 - seda://test] parallel                                 : Received 0, processing
2016-08-06 11:45:08.872  INFO 27410 --- [4 - seda://test] parallel                                 : Received 2, processing
2016-08-06 11:45:08.872  INFO 27410 --- [3 - seda://test] parallel                                 : Received 1, processing
2016-08-06 11:45:08.872  INFO 27410 --- [2 - seda://test] parallel                                 : Received 3, processing
2016-08-06 11:45:08.872  INFO 27410 --- [5 - seda://test] parallel                                 : Received 4, processing
2016-08-06 11:45:13.876  INFO 27410 --- [1 - seda://test] parallel                                 : Processed 0
2016-08-06 11:45:13.876  INFO 27410 --- [3 - seda://test] parallel                                 : Processed 1
2016-08-06 11:45:13.876  INFO 27410 --- [4 - seda://test] parallel                                 : Processed 2
2016-08-06 11:45:13.876  INFO 27410 --- [5 - seda://test] parallel                                 : Processed 4
2016-08-06 11:45:13.876  INFO 27410 --- [2 - seda://test] parallel                                 : Processed 3
2016-08-06 11:45:13.877  INFO 27410 --- [r://testStarter] test timer                               : Matched? true

您会注意到NotifyBuilder在结果匹配后如何返回结果。

答案 1 :(得分:0)

如果您知道您正在使用的每批消息都包含X消息,则可以在并行处理结束时使用aggregator。对于您的示例,每组消息都有自己唯一的标头标记,将由聚合器选取。在所有消息都是进程并且所有消息都已在聚合器中结束之后,您可以将消息聚合到您想要的任何格式并返回它们。