2017-02-06 3 views
0

Я пытаюсь преобразовать следующий проект интеграции Spring в конфигурационную версию java, используя Spring Integration dsl. Мне не повезло, и я не могу найти документацию на dsl, которая помогает мне понять структуру, достаточную для того, чтобы пройти через это.Spring IntegrationFlow http request to amqp queue

Вот проект, который я конвертирую. Он использует xml config. https://github.com/dsyer/http-amqp-tunnel

В основном требуется HTTP-запрос, а затем туннелирует его через rabbitmq в целевое приложение с другой стороны. Хорошее описание того, что должен сделать проект, можно найти по ссылке выше.

Основные отличия между моим приложением и тем, что я указал на github, приведенные выше, состоят в том, что моя основана на весенней загрузке 1.5.1.RELEASE, а оригинал - на 1.1.4.BUILD-SNAPSHOT. Кроме того, в исходном проекте используется поддержка пространства имен xml-интеграции Spring, а именно int-http: входящий-шлюз, int-http: исходящий-шлюз, int-amqp: исходящий-шлюз и int-amqp: входящий-шлюз, тогда как я использую IntegrationFlow dsl в конфигурации java.

Мой код никогда не помещает сообщение в 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.GenericMessaging[email protected], errorChannel=org.springframewor[email protected]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 

Config на основе комментариев Гари Modified ударить/фасоль конечных пружинной загрузки привода.

@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(); 
} 

ответ

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

EDIT

И с 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(); 
    } 

} 
+0

Gary Я попробовал вашу конфигурацию, и я получаю сообщение об ошибке. Похоже, мне может понадобиться конвертер для преобразования сообщения в json-сообщение для RestTemplate. Любые указания относительно того, какой компонент подключиться к этому? org.springframework.web.client.RestClientException: Не удалось написать запрос: не найдено подходящего HttpMessageConverter для типа запроса [org.springframework.util.LinkedMultiValueMap] и типа содержимого [application/x-java-serialized-object]. –

+0

С другой стороны, это не запрос на отдых, поступающий из браузера, так как я буквально просто делаю GET из браузера. –

+0

Я также добавил обновленную конфигурацию на основе вашего комментария. На самом деле просто изменили URL-адреса, чтобы попасть в конечную точку исполнительного механизма. –

1

Для взаимодействия с запросом/ответом вы должны использовать Amqp.outboundGateway. Это то, что у Дэйва в образце:

<int-amqp:outbound-gateway request-channel="outbound" 
     routing-key="${outboundQueue}" mapped-request-headers="http_*" /> 

Plus, посмотрите, вы пропустили здесь в routingKey, который по логике Дэйва должны быть 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, а также. И его компоненты выглядят хорошо для меня.

Вы действительно ожидаете ответа от службы отдыха, поэтому все ваши компоненты, расположенные ниже по потоку, должны быть запрошены/отвечены.

Просто давайте взглянем на дизайн еще раз!

------------- HTTP ------------- AMQP ------------- AMQP ------------- HTTP -------------- 
| local app | <------> | client | <------> | broker | <------> | server | <------> | target app | 
-------------   -------------   -------------   -------------   -------------- 
+0

См. Мой ответ. –

+0

Спасибо за информацию. Думаю, я стал дальше. Я обновил свой первоначальный вопрос, включив в него мою новую конфигурацию, основанную на ваших советах. Теперь я получаю сообщение об ошибке, которое повторяется в журнале. –

+0

Я все еще вижу там 'Amqp.outboundAdapter', но не' Amqp.outboundGateway', как я вам рекомендую. Плюс для этих 'wireTap' без неявного канала вы должны использовать '.bridge (null)', как предлагает Гэри. Хотя вы должны иметь в виду, что «wireTap» применяется к каналу в том месте, где вы указываете его, а не весь поток. –

Смежные вопросы