Camel JMS异步请求回复

时间:2017-06-14 10:43:45

标签: spring-boot apache-camel

我正在尝试实现从远程系统队列(System.A.out)读取请求消息的Camel路由该路由查看消息正文并动态地将其路由到另一个系统queue(System.B.in)然后该路由完成,并等待其队列中的下一条消息(当前它阻塞并等待临时队列的响应)

System.B读取其队列中的内容(System.B.in,并非始终是驼峰路由)处理消息并在其出队列(System.B.out)系统上删除响应。 B使用请求消息中的JMSMessageID作为其响应上的JMSCorrelationID,即保留该请求的所有内容。

Camel Route(类似于System.A.out,但是监听System.B.out)获取响应消息并使用JMSCorrelationID(请求将没有JMSCorrelationID,因此将通过消息路由body)找到请求的JMSReplyTo队列(System.A.in)并删除System.A队列中的响应,以便System.A进行处理。

我使用的是SpringBoot和Camel 2.18.3,消息队列是IMB MQ版本8

我的路线如下:

@Override
public void configure() throws Exception {

    //@formatter:off
    Predicate validRoute = header("route-valid").isEqualTo(true);
    Predicate inValidRoute = header("route-valid").isEqualTo(false);
    Predicate splitRoute = header("route-split").isEqualTo(true);
    Predicate singleRoute = header("route-split").isEqualTo(false);
    Predicate validSplitRoute = PredicateBuilder.and(validRoute, splitRoute);
    Predicate validSingelRoute = PredicateBuilder.and(validRoute, singleRoute);

    from(endpoint(incomingURI)).routeId(routeId)
        .process(exchange -> {
                exchange.getIn().setHeader("route-source", format("%s-%s", incomingURI, routeId));
            })
            .to(endpoint(format("bean:evaluateIncomingMessageService?method=routeMessage(*, %s)", replyToURI)))
            .choice()
                .when(validSingelRoute)
                    .log(DEBUG, "Creating a Single route")
                    .to(endpoint("bean:messageCoalitionService?method=saveInstruction(*)"))
                    .setExchangePattern(ExchangePattern.InOut)
                    .toD("${header.route-recipients}")
                .when(inValidRoute)
                    .log(DEBUG, "a.b.test", format("Incoming message [%s] failed evaluation: %s", incomingURI, body()))
                    .to(endpoint(deadLetterURI))
                    .routeId(format("%s-%s", incomingURI, routeId))
                .when(validSplitRoute)
                    .log(DEBUG, "Creating a Split route")
                    .to(endpoint("bean:messageCoalitionService?method=saveInstructions(*)"))
                    .setExchangePattern(ExchangePattern.InOut)
                    .multicast()
                    .toD("${header.route-recipients}").endChoice()
                .otherwise()
                    .log(DEBUG, "a.b.test", format("Incoming message [%s] failed evaluation: %s", incomingURI, body()))
                    .to(endpoint(deadLetterURI))
                    .routeId(format("%s-%s", incomingURI, routeId));

Spring Bean evaluateIncomingMessageService决定消息是请求(无关联ID)还是响应,并为请求设置路由标头。我希望Camel能自动将响应路由到Request.JMSReplyTo队列,如果没有,怎么能这样做呢?

在Camel Route构建器中配置replyToURI,如果路由侦听System.A.out,则其replyToURI将始终为System.A.in。

evaluateIncomingMessageService.routeMessage如下所示:

 public void routeMessage(final Exchange exchange, final String replyToURI) {
    String correlationId = exchange.getIn().getHeader("JMSCorrelationID", String.class);

    if (correlationId != null) {
        log.debug("Processing Message Response with JMSCorrelationID [{}]", correlationId);
        exchange.getIn().setHeader("JMSReplyTo", replyToURI);
    } else {
        // Request Messages have nave NO correlationId
        log.debug("Processing Message Request with MessageID [{}] and JMSMessageID: [{}]",
                exchange.getIn().getMessageId(),
                exchange.getIn().getHeader("JMSMessageID") != null ? exchange.getIn().getHeader("JMSMessageID").toString() : exchange.getIn().getMessageId());
        String message = exchange.getIn().getBody(String.class);
        Set<ContentBasedRoute> validRoutes = contentBasedRouting
                .stream().filter(
                        routeEntity -> Pattern.compile(
                                routeEntity.getRegularExpression(), DOTALL).matcher(message).matches()).collect(Collectors.toSet());

        if (validRoutes.isEmpty()) {
            log.warn("No valid routes found for message: [{}] ", message);
            exchange.getIn().setHeader("route-valid", false);

        } else {
            HashMap<String, ContentBasedRoute> uniqueRoutes = new HashMap<>();
            validRoutes.stream().forEach(route -> uniqueRoutes.putIfAbsent(route.getDestination(), route));

            exchange.getIn().setHeader("route-valid", true);
            exchange.getIn().setHeader("route-count", uniqueRoutes.size());
            exchange.getIn().setHeader("JMSReplyTo", replyToURI);
            //if (exchange.getIn().getHeader("JMSMessageID") == null) {
             //   exchange.getIn().setHeader("JMSMessageID", exchange.getIn().getMessageId());
            //}
            if (uniqueRoutes.size() > 1) {
                log.debug("Building a split route");
                StringBuilder routes = new StringBuilder();
                StringBuilder routeIds = new StringBuilder();
                StringBuilder routeRegex = new StringBuilder();
                uniqueRoutes.keySet().stream().forEach(i -> routes.append(i).append(","));
                uniqueRoutes.values().stream().forEach(j -> routeIds.append(j.getRouteId()).append(","));
                uniqueRoutes.values().stream().forEach(k -> routeRegex.append(k.getRegularExpression()).append(","));
                routes.deleteCharAt(routes.length() - 1);
                routeIds.deleteCharAt(routeIds.length() - 1);
                routeRegex.deleteCharAt(routeRegex.length() - 1);

                exchange.getIn().setHeader("route-split", true);
                exchange.getIn().setHeader("route-uuid", routeIds.toString());
                exchange.getIn().setHeader("route-regex", routeRegex.toString());
                exchange.getIn().setHeader("route-recipients", routes.toString());
            } else {
                exchange.getIn().setHeader("route-split", false);
                exchange.getIn().setHeader("route-uuid", uniqueRoutes.values().iterator().next().getRouteId());
                exchange.getIn().setHeader("route-regex", uniqueRoutes.values().iterator().next().getRegularExpression());
                exchange.getIn().setHeader("route-recipients", uniqueRoutes.values().iterator().next().getDestination());
            }
        }
    }
}

Bean messageCoalitionService只保存邮件正文和标题,以便可以复制邮件并审核系统。

我不确定我是否错误地解决了这个问题,我应该使用Camel Async API还是需要管道来实现这个?这个模式看起来接近我需要的http://camel.apache.org/async.html(异步请求回复)任何帮助都会非常感谢。

1 个答案:

答案 0 :(得分:0)

最后,我使用Spring Integration实现了上述功能。一旦Camel Route发送了消息,我就无法找到检索已发送消息的消息ID的方法,这意味着在发送响应时我无法跟踪关联ID。使用Camel InOut导致Camel阻塞并等待响应,这也不是我想要的。

感谢lutalex提供此解决方案: http://forum.spring.io/forum/other-spring-related/remoting/30397-jmsmessageid-after-message-is-sent?p=745127#post745127