Spring IntegrationFlow对amqp队列的http请求

Spring IntegrationFlow对amqp队列的http请求,spring,spring-boot,spring-integration,Spring,Spring Boot,Spring Integration,我正在尝试使用SpringIntegrationDSL将下面的spring集成项目转换为java配置版本。我运气不太好,也找不到有关dsl的文档来帮助我理解框架,从而完成这项工作 这是我正在转换的项目。它使用xml配置 基本上,它接受一个http请求,然后通过rabbitmq将其隧道到另一端的目标应用程序。在上面的链接中可以找到项目应该做什么的详细描述 我的应用程序与上面列出的github上的应用程序之间的主要区别在于,我的应用程序基于spring boot 1.5.1.RELEASE,而原来的

我正在尝试使用SpringIntegrationDSL将下面的spring集成项目转换为java配置版本。我运气不太好,也找不到有关dsl的文档来帮助我理解框架,从而完成这项工作

这是我正在转换的项目。它使用xml配置

基本上,它接受一个http请求,然后通过rabbitmq将其隧道到另一端的目标应用程序。在上面的链接中可以找到项目应该做什么的详细描述

我的应用程序与上面列出的github上的应用程序之间的主要区别在于,我的应用程序基于spring boot 1.5.1.RELEASE,而原来的应用程序基于1.1.4.BUILD-SNAPSHOT。另外,原始项目使用spring集成xml命名空间支持,即int-http:inbound gateway、int-http:outbound gateway、int-amqp:outbound gateway和int-amqp:inbound gateway,而我在java配置中使用IntegrationFlow dsl

我的代码甚至从未在RabbitMQ上放置消息,我在浏览器中遇到超时异常,因此我认为我的IntegrationFlow设置不正确。我添加了一个记录请求的有线点击,当我从浏览器点击应用程序时,我只看到一个有线点击的输出

只要朝着正确的方向轻推一下就好了

更新配置和错误

以下错误消息将继续打印,并且在应用程序运行时,RabbitMQ上会保留一条消息。它看起来像是把它拉下来,得到一个错误,然后把它放回去。这与我有关,因为我希望任何错误都能传播回原始客户机,而不会使服务器陷入困境

2017-02-06 16:00:12.167  INFO 10264 --- [nio-9000-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-02-06 16:00:12.167  INFO 10264 --- [nio-9000-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-02-06 16:00:12.190  INFO 10264 --- [nio-9000-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 23 ms
2017-02-06 16:00:16.806  INFO 10264 --- [erContainer#0-1] outbound                                 : <200 OK,{X-Application-Context=[application], Content-Type=[text/html;charset=UTF-8], Content-Length=[14], Date=[Mon, 06 Feb 2017 22:00:16 GMT]}>
2017-02-06 16:00:16.810  WARN 10264 --- [erContainer#0-1] s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message listener failed.

org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:872) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:782) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:702) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:95) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:186) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1227) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:683) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1181) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1165) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1500(SimpleMessageListenerContainer.java:95) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1367) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at java.lang.Thread.run(Unknown Source) [na:1.8.0_66]
Caused by: org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application:test:9000.amqpInboundGateway.channel#1'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload=<200 OK,{X-Application-Context=[application], Content-Type=[text/html;charset=UTF-8], Content-Length=[14], Date=[Mon, 06 Feb 2017 22:00:16 GMT]}>, headers={http_requestMethod=GET, replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@2eb9b1c6, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@2eb9b1c6, amqp_consumerQueue=request, http_requestUrl=http://localhost:9000/tunnel/, id=bcb94ed9-45fc-c333-afee-de6e20a9f1b5, Content-Length=14, amqp_consumerTag=amq.ctag-ncEDSKdgWNKQk-jhGfqsbw, contentType=text/html;charset=UTF-8, http_statusCode=200, Date=1486418416000, timestamp=1486418416805}]
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:93) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115) ~[spring-messaging-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45) ~[spring-messaging-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105) ~[spring-messaging-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:292) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:212) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:129) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115) ~[spring-messaging-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:150) ~[spring-messaging-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:45) ~[spring-messaging-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.messaging.core.AbstractMessagingTemplate.sendAndReceive(AbstractMessagingTemplate.java:42) ~[spring-messaging-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.integration.core.MessagingTemplate.sendAndReceive(MessagingTemplate.java:97) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.gateway.MessagingGatewaySupport.doSendAndReceive(MessagingGatewaySupport.java:441) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.gateway.MessagingGatewaySupport.sendAndReceiveMessage(MessagingGatewaySupport.java:409) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.amqp.inbound.AmqpInboundGateway.access$400(AmqpInboundGateway.java:52) ~[spring-integration-amqp-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.amqp.inbound.AmqpInboundGateway$1.onMessage(AmqpInboundGateway.java:154) ~[spring-integration-amqp-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:779) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    ... 10 common frames omitted
Caused by: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:154) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89) ~[spring-integration-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    ... 35 common frames omitted
配置基于Gary的评论 修改为命中spring引导执行器的/beans端点

@Bean
public IntegrationFlow webToRabbit(RabbitTemplate amqpTemplate) {
    return IntegrationFlows.from(Http.inboundGateway("/tunnel"))
            .log()
            .handle(Amqp.outboundGateway(amqpTemplate).routingKey(queue().getName()))
            .log()
            .bridge(null)
            .get();
}

@Bean
public Queue queue() {
    return new AnonymousQueue();
}

@Bean
public IntegrationFlow rabbitToWeb(ConnectionFactory connectionFactory) {
    return IntegrationFlows.from(Amqp.inboundGateway(connectionFactory, queue()))
            .log()
            .handle(Http.outboundGateway("http://localhost:8080/beans")
                    .expectedResponseType(String.class))
            .log()
            .bridge(null)
            .get();
}

@Bean
public IntegrationFlow finalWeb() {
    return IntegrationFlows.from(Http.inboundGateway("/beans"))
            .log()
            .<String, String>transform(String::toUpperCase)
            .log()
            .bridge(null)
            .get();
}

对于请求/回复交互,您必须使用Amqp.outboundGateway。这就是Dave在其样本中的内容:

<int-amqp:outbound-gateway request-channel="outbound"
      routing-key="${outboundQueue}" mapped-request-headers="http_*" />
服务器部分也可以组合到单个IntegrationFlow中。 它的部件对我来说很好

您确实需要rest服务的回复,因此,所有下游组件都必须是request/reply

<>让我们再一次看一下设计!
-------------   HTTP   -------------   AMQP    -------------   AMQP   -------------   HTTP   --------------
| local app | <------> |   client  | <------>  |   broker  | <------> |   server  | <------> | target app | 
-------------          -------------           -------------          -------------          --------------
编辑

使用JSON

OTA应用 公共类SO42077149应用程序{

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

    @Bean
    public IntegrationFlow webToRabbit(RabbitTemplate amqpTemplate) {
        return IntegrationFlows.from(Http.inboundGateway("/foo"))
                .log()
                .handle(Amqp.outboundGateway(amqpTemplate)
                        .routingKey(queue().getName())
                        .mappedRequestHeaders("*")
                        .mappedReplyHeaders("*"))
                .log()
                .bridge(null)
                .get();
    }

    @Bean
    public Queue queue() {
        return new AnonymousQueue();
    }

    @Bean
    public IntegrationFlow rabbitToWeb(ConnectionFactory connectionFactory) {
        return IntegrationFlows.from(Amqp.inboundGateway(connectionFactory, queue()))
                .log()
                .handle(Http.outboundGateway("http://localhost:8080/bar")
                        .mappedRequestHeaders("*")
                        .mappedResponseHeaders("*")
                        .httpMethod(HttpMethod.GET)
                        .expectedResponseType(Map.class))
                .log()
                .log(Level.INFO, "payloadClass", "payload.getClass()")
                .bridge(null)
                .get();
    }

    @Bean
    public IntegrationFlow finalWeb() {
        return IntegrationFlows.from(Http.inboundGateway("/bar"))
                .log()
                .transform("{ \"foo\" : \"bar\" }")
                .enrichHeaders(h -> h.header("contentType", "application/json"))
                .log()
                .bridge(null)
                .get();
    }

}

我也看到了我的答案。谢谢你提供的信息。我想我得到了进一步的信息。我根据你的建议更新了我的原始问题以包括我的新配置。我现在得到了一个在日志中重复出现的错误。我仍然看到了Amqp.outboundAdapter,但不是我推荐的Amqp.outboundGateway。另外,对于那些没有隐式通道的窃听,你必须使用.bridgenull正如Gary所建议的。虽然您应该记住,wireTap应用于您指出的频道,但不是整个流。抱歉。我的疏忽。它现在正在命中目标应用程序,但得到了405。原因:org.springframework.messaging.MessageHandlingException:URI的HTTP请求执行失败[;嵌套的异常是org.springframework.web.client.httpclienterror异常:org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessageHttpRequestExecutingMessageHandler.java:409~[spring-integration-http-4.3.7.RELEASE.jar:4.3.7.RELEASE]。如果我直接点击执行器上的/beans端点,它就会工作。在我看来,它没有通过通道传输HTTP方法,可能会返回到默认的POST,而/beans方法不支持。我需要做些什么来保留初始HTTP方法吗?Gary,我尝试了您的配置,我得到了一个错误。我可能需要一个转换器来将消息转换为RestTemplate的json消息。有关插入哪个组件的任何指导信息?org.springframework.web.client.RestClientException:无法写入请求:找不到适合请求类型[org.springframework.util.LinkedMultiValueMap]的HttpMessageConverter和内容类型[应用程序/x-java-serialized-object]。另一方面,这不是来自浏览器的rest请求,因为我实际上只是从浏览器获取。我也根据您上面的评论放置了更新的配置。实际上只是修改了URL以命中执行器端点。我需要查看完整的堆栈跟踪和网络监视器;鉴于您正在命中/beans执行器端点,不清楚您为什么保留我的finalWeb@Bean。我尝试了一些方法,通过手工制作一些json来重现您的问题,将响应类型更改为映射,复制更多的标题等,并且在查看编辑时没有遇到任何问题。由于某些原因,我无法让/beans执行器工作;不确定那里发生了什么。如果您可以将项目发布到某个地方,我可以我还没有试过你最新的代码片段。我会试一试,让你知道这是否有效。
-------------   HTTP   -------------   AMQP    -------------   AMQP   -------------   HTTP   --------------
| local app | <------> |   client  | <------>  |   broker  | <------> |   server  | <------> | target app | 
-------------          -------------           -------------          -------------          --------------
@SpringBootApplication
public class So42077149Application {

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

    @Bean
    public IntegrationFlow webToRabbit(RabbitTemplate amqpTemplate) {
        return IntegrationFlows.from(Http.inboundGateway("/foo"))
                .log()
                .handle(Amqp.outboundGateway(amqpTemplate).routingKey(queue().getName()))
                .log()
                .bridge(null)
                .get();
    }

    @Bean
    public Queue queue() {
        return new AnonymousQueue();
    }

    @Bean
    public IntegrationFlow rabbitToWeb(ConnectionFactory connectionFactory) {
        return IntegrationFlows.from(Amqp.inboundGateway(connectionFactory, queue()))
                .log()
                .handle(Http.outboundGateway("http://localhost:8080/bar")
                        .expectedResponseType(String.class))
                .log()
                .bridge(null)
                .get();
    }

    @Bean
    public IntegrationFlow finalWeb() {
        return IntegrationFlows.from(Http.inboundGateway("/bar"))
                .log()
                .<String, String>transform(String::toUpperCase)
                .log()
                .bridge(null)
                .get();
    }


}
$ curl -H "Content-Type: text/plain" -d foo localhost:8080/foo
FOO
    public static void main(String[] args) {
        SpringApplication.run(So42077149Application.class, args);
    }

    @Bean
    public IntegrationFlow webToRabbit(RabbitTemplate amqpTemplate) {
        return IntegrationFlows.from(Http.inboundGateway("/foo"))
                .log()
                .handle(Amqp.outboundGateway(amqpTemplate)
                        .routingKey(queue().getName())
                        .mappedRequestHeaders("*")
                        .mappedReplyHeaders("*"))
                .log()
                .bridge(null)
                .get();
    }

    @Bean
    public Queue queue() {
        return new AnonymousQueue();
    }

    @Bean
    public IntegrationFlow rabbitToWeb(ConnectionFactory connectionFactory) {
        return IntegrationFlows.from(Amqp.inboundGateway(connectionFactory, queue()))
                .log()
                .handle(Http.outboundGateway("http://localhost:8080/bar")
                        .mappedRequestHeaders("*")
                        .mappedResponseHeaders("*")
                        .httpMethod(HttpMethod.GET)
                        .expectedResponseType(Map.class))
                .log()
                .log(Level.INFO, "payloadClass", "payload.getClass()")
                .bridge(null)
                .get();
    }

    @Bean
    public IntegrationFlow finalWeb() {
        return IntegrationFlows.from(Http.inboundGateway("/bar"))
                .log()
                .transform("{ \"foo\" : \"bar\" }")
                .enrichHeaders(h -> h.header("contentType", "application/json"))
                .log()
                .bridge(null)
                .get();
    }

}