簡體   English   中英

Spring IntegrationFlow http請求到amqp隊列

[英]Spring IntegrationFlow http request to amqp queue

我正在嘗試使用Spring Integration dsl將以下spring集成項目轉換為java配置版本。 我運氣不太好,我在dsl上找不到可以幫助我充分理解框架的文檔。

這是我要轉換的項目。 它使用xml配置。 https://github.com/dsyer/http-amqp-tunnel

基本上,它接受一個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設置不正確。 我添加了一個記錄請求的電線,當我從瀏覽器中點擊該應用程序時,我只會看到一個電線的輸出。

只要朝着正確的方向輕推,我們將不勝感激。

更新了配置和錯誤

package org.springframework.platform.proxy;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.*;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.*;
import org.springframework.integration.dsl.amqp.Amqp;
import org.springframework.integration.dsl.http.Http;
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.messaging.MessageHandler;
import org.springframework.web.client.RestTemplate;

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableIntegration
public class TunnelApplication 
{
    public static void main(String[] args) 
    {
        SpringApplication.run(TunnelApplication.class, args);
    }

    @Value("${urlExpression}")
    private String urlExpression;

    @Value("${targetUrl}")
    private String targetUrl;

    @Value("${outboundQueue}")
    private String outboundQueue;

    @Value("${inboundQueue}")
    private String inboundQueue;

    @Autowired
    private ConnectionFactory rabbitConnectionFactory;

    @Bean
    public Queue requestQueue() 
    {
        return new Queue(outboundQueue, true, false, true);
    }

    @Bean
    public Queue targetQueue() 
    {
        return new Queue(inboundQueue, true, false, true);
    }

    @Bean
    public RestTemplate safeRestTemplate()
    {
        return new RestTemplate();
    }

    @Bean
    public Jackson2JsonMessageConverter jsonMessageConverter()
    {
        return new Jackson2JsonMessageConverter();
    }


    @Bean
    public AmqpTemplate amqpTemplate()
    {
        RabbitTemplate result = new RabbitTemplate(rabbitConnectionFactory);
        result.setMessageConverter(jsonMessageConverter());
        return result;
    }

    @Bean
    public IntegrationFlow httpInboundGateway()
    {
        return IntegrationFlows
                .from(Http.inboundGateway("/tunnel"))
                .handle(
                        Amqp.outboundAdapter(amqpTemplate())
                            .mappedRequestHeaders("http_*")
                            .routingKey(outboundQueue)
//                          .routingKeyExpression("headers['routingKey']")
                        )
                .wireTap(f->f.handle(logger("outbound")))
                .get();
    }

    @Bean
    public IntegrationFlow amqpInboundGateway(ConnectionFactory connectionFactory) 
    {
        return IntegrationFlows.from
                (
                    Amqp.inboundGateway(connectionFactory, inboundQueue)
                        .mappedRequestHeaders("http_*")
                        .messageConverter(jsonMessageConverter())
                )
                .handle(Http.outboundGateway(targetUrl))
                .wireTap(f->f.handle(logger("inbound")))
                .get();
    }


    @Bean
    public MessageHandler logger(String name) 
    {
         LoggingHandler loggingHandler =  new LoggingHandler(LoggingHandler.Level.INFO.name());
         loggingHandler.setLoggerName(name);
         return loggingHandler;
    }
}

繼續打印以下錯誤消息,並且在應用程序運行時,有一條消息停留在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的評論進行配置修改為觸及彈簧啟動執行器的/ 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_*" />

另外,看,您在這里已經錯過了routingKey ,根據Dave的邏輯,該鍵必須為outboundQueue

Http.inboundGateway和該Amqp.outboundGateway可以組合為一個IntegrationFlow

@Bean
public IntegrationFlow clientGateway() {
    return IntegrationFlows
            .from(Http.inboundGateway("/tunnel"))
            .handle(Amqp.outboundGateway(amqpTemplate)
                        .mappedRequestHeaders("http_*")
                        .routingKey(outboundQueue))
            .get();
}

服務器部分也可以組合到單個IntegrationFlow中。 它的組件對我來說很好。

您確實希望得到REST服務的答復,因此,所有下游組件都必須是請求/答復。

讓我們再來看看設計吧!

-------------   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

編輯

並使用JSON ...

otApplication公共類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())
                        .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();
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM