2016-07-29 20 views
1

Ich habe zwei Spring MVC-Anwendungen; Ich verwende Spring Integration HTTP, um sie zu integrieren.Spring Integration: Inbound-Gateway-Management ist um 20 Sekunden langsamer als @ Controller-Management

Der Prozess

Der Prozess ist sehr einfach: eine HTTP-GET-Anforderung an den "application1" unter der Regie von einem @Controller verwaltet wird und "übertragen", dann auf den "Application2" mit einem <int-http:outbound-gateway> durch einen HTTP-POST.

Dort angekommen, diese POST (Zwischen-) Anforderung an einen anderen URI des „Application2“ (mit einem anderen <int-http:outbound-gateway>) verwaltet und übertragen, die die reale Antwort zur Verfügung stellt.

Die Ausgabe

ich ein anderes Verhalten im Anforderungsmanagement bemerkt habe auf der „Application2“ Seite (die durch die <int-http:outbound-gateway> des „application1“ aufgerufen wird).

Wenn ich eine @Controller verwenden, um den "Zwischen" URI zu verwalten, benötigt die Prozessausführung einige Millisekunden.

Aber wenn ich eine <int-http:inbound-gateway> verwenden, um den gleichen Prozess zu replizieren, benötigt es 20 Sekunden, um den Prozess zu vervollständigen: Warum dieser Unterschied?

Dies ist die Umsetzung mit dem @Controller:

@Controller 
@RequestMapping(value="/service") 
public class ServiceController { 

    @RequestMapping(value="/{resource}", method=RequestMethod.POST) 
    public void resource(@PathVariable(value="resource") String resource, HttpServletRequest request, HttpServletResponse res){ 
     handleRequest(resource,request, res); 
    } 

    private void handleRequest(String resource, HttpServletRequest request, HttpServletResponse res) throws Exception { 
     // READING THE REQUEST PAYLOAD 
     RequestHandler requestHandler = RequestHandler.getHandler(request);   
     RequestPayload payload = requestHandler.getPayload(); 

     // PREPARING THE MESSAGE TO THE CHANNEL 
     MessagingChannel messagingChannel = (MessagingChannel)ApplicationContextResolver.getApplicationContext().getBean("requestChannelBean"); 
     MessageChannel requestChannel = messagingChannel.getRequestChannel(); 
     MessagingTemplate messagingTemplate = new MessagingTemplate(); 
     String url = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getServletContext().getContextPath()+request.getServletPath()+"/"+resource; 
     Message<?> requestMessage = MessageBuilder.withPayload(url).build(); 

     // SEND THE MESSAGE AND RECEIVE THE RESPONSE 
     Message<?> response = messagingTemplate.sendAndReceive(requestChannel, requestMessage); 

     // PRINT THE RESPONSE 
     res.getWriter().write((String)response.getPayload()); 

    } 
} 

Diese Lösung funktioniert perfekt und es braucht ein paar Millisekunden, um die Antwort zurück.

Das Folgende ist die Umsetzung mit dem <int-http:inbound-gateway> und der <int:service-activator>, die ich replizieren die @Controller Lösung zu verwenden, ich versuche:

<int:service-activator id="channelServiceActivator" 
    ref="channelService" 
    input-channel="requestChannel"  
    method="manage"/> 

<int-http:inbound-gateway id="gateway" request-channel="requestChannel" 
       path="/service/**" 
       supported-methods="POST" 
       header-mapper="headerMapper"> 
    <int-http:header name="requestAttributes" expression="#requestAttributes"/> 
</int-http:inbound-gateway> 

Mit diesem Service-Aktivator:

public class ChannelService { 

    public String manage(Message<?> message) throws Throwable{ 

     // RECOVERING THE HttpServletRequest 
     ServletRequestAttributes sra = (ServletRequestAttributes) message.getHeaders().get("requestAttributes"); 
     HttpServletRequest request = sra.getRequest(); 

     // RETRIEVING THE PAYLOAD 
     Object payload = message.getPayload(); 

     // PREPARING THE MESSAGE TO THE CHANNEL 
     MessagingChannel messagingChannel = (MessagingChannel)ApplicationContextResolver.getApplicationContext().getBean("requestChannelBean"); 
     MessageChannel requestChannel = messagingChannel.getRequestChannel(); 
     MessagingTemplate messagingTemplate = new MessagingTemplate(); 
     String url = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getServletContext().getContextPath()+request.getServletPath()+"/"+resource; 
     Message<?> requestMessage = MessageBuilder.withPayload(url).build(); 

     // SEND THE MESSAGE AND RECEIVE THE RESPONSE 
     Message<?> response = messagingTemplate.sendAndReceive(requestChannel, requestMessage); 

     // RETURN THE RESPONSE 
     return (String) response.getPayload(); // <- since here, the behaviour is normal. 
               // The framework needs 20 seconds after this instruction 

    } 
} 

Diese Implementierung benötigt 20 Sekunden für jede Anfrage, ich verstehe den Grund nicht Das. Ich sehe keinen Fehler in den Spring-Logs, ich sehe nur, dass das System zu "frieren" scheint: Es protokolliert nichts mehr für 20 Sekunden.

Jede Erklärung würde geschätzt werden.

UPDATE

die Protokolle hier. Nach dem "HttpMessageConverterExtractor.extractData: 101", benötigt es 20 Sekunden vor dem Anzeigen der anderen "org.springframework.integration.http" Protokolle (2016-07-29 15:06:18 -> 2016-07-29 15:06 : 38).

2016-07-29 15:06:18 TRACE [] org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.close:217 (http-nio-8080-exec-4) - Logical connection closed 
GenericMessage [payload={ 
    "_embedded" : { 
    "persons" : [ ] 
    }, 
    "_links" : { 
    "self" : { 
     "href" : "http://localhost:8080/application2/api/persons" 
    }, 
    "profile" : { 
     "href" : "http://localhost:8080/application2/api/profile/persons" 
    }, 
    "search" : { 
     "href" : "http://localhost:8080/application2/api/persons/search" 
    } 
    } 
}, headers={Transfer-Encoding=chunked, Server=Apache-Coyote/1.1, Accept=application/json, X-Content-Type-Options=nosniff, Pragma=no-cache, http_statusCode=200, Date=1469797578000, X-Frame-Options=DENY, Cache-Control=no-cache, no-store, max-age=0, must-revalidate, id=2b6b0b80-82bc-2ce7-c364-1e84c389bc71, X-XSS-Protection=1; mode=block, contentType=application/json, timestamp=1469797578411}] 
2016-07-29 15:06:18 DEBUG [] org.springframework.web.client.RestTemplate.handleResponse:658 (executor-1) - POST request for "http://localhost:8080/application2/api/service/persons" resulted in 200 (OK) 
2016-07-29 15:06:18 DEBUG [] org.springframework.web.client.HttpMessageConverterExtractor.extractData:101 (executor-1) - Reading [java.lang.String] as "application/octet-stream" using [[email protected]97e277] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:436 (executor-1) - inboundHeaderNames=[*] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[server] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[Server], value=[Apache-Coyote/1.1] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[pragma] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[Pragma], value=no-cache 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[accept] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[Accept], value=[application/json] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[host] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[host], value=[localhost:8080] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[connection] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[connection], value=[keep-alive] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[cache-control] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[Cache-Control], value=no-cache 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[user-agent] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[user-agent], value=[Java/1.8.0_65] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[x-xss-protection] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:454 (executor-1) - setting headerName=[X-XSS-Protection], value=[1; mode=block] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[x-frame-options] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:454 (executor-1) - setting headerName=[X-Frame-Options], value=[DENY] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[x-content-type-options] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:454 (executor-1) - setting headerName=[X-Content-Type-Options], value=[nosniff] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[content-type] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[Content-Type], value=application/octet-stream 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[content-length] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[Content-Length], value=968 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.shouldMapHeader:537 (executor-1) - headerName=[date] WILL be mapped, matched pattern=* 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:463 (executor-1) - setting headerName=[Date], value=1.469.797.578.000 
answer: { 
    "_embedded" : { 
    "persons" : [ ] 
    }, 
    "_links" : { 
    "self" : { 
     "href" : "http://localhost:8080/application2/api/persons" 
    }, 
    "profile" : { 
     "href" : "http://localhost:8080/application2/api/profile/persons" 
    }, 
    "search" : { 
     "href" : "http://localhost:8080/application2/api/persons/search" 
    } 
    } 
} 
2016-07-29 15:06:38 TRACE [] org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest:138 (http-nio-8080-exec-2) - Method [sendMessageToChannel] returned [null] 
2016-07-29 15:06:38 DEBUG [] org.springframework.web.servlet.DispatcherServlet.processDispatchResult:1044 (http-nio-8080-exec-2) - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling 
2016-07-29 15:06:38 TRACE [] org.springframework.web.servlet.FrameworkServlet.resetContextHolders:1062 (http-nio-8080-exec-2) - Cleared thread-bound request context: SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.context.HttpSessi[email protected]9bacc4f] 
2016-07-29 15:06:38 DEBUG [] org.springframework.web.servlet.FrameworkServlet.processRequest:1000 (http-nio-8080-exec-2) - Successfully completed request 
2016-07-29 15:06:38 TRACE [] org.springframework.context.support.AbstractApplicationContext.publishEvent:362 (http-nio-8080-exec-2) - Publishing event in WebApplicationContext for namespace 'dispatcher-servlet': ServletRequestHandledEvent: url=[/application1/api/persons/]; client=[0:0:0:0:0:0:0:1]; method=[GET]; servlet=[dispatcher]; session=[null]; user=[null]; time=[21426ms]; status=[OK] 
2016-07-29 15:06:38 TRACE [] org.springframework.context.support.AbstractApplicationContext.publishEvent:362 (http-nio-8080-exec-2) - Publishing event in Root WebApplicationContext: ServletRequestHandledEvent: url=[/application1/api/persons/]; client=[0:0:0:0:0:0:0:1]; method=[GET]; servlet=[dispatcher]; session=[null]; user=[null]; time=[21426ms]; status=[OK] 
2016-07-29 15:06:38 DEBUG [] org.springframework.security.web.access.ExceptionTranslationFilter.doFilter:117 (http-nio-8080-exec-2) - Chain processed normally 
2016-07-29 15:06:38 DEBUG [] org.springframework.security.web.header.writers.HstsHeaderWriter.writeHeaders:130 (http-nio-8080-exec-2) - Not injecting HSTS header since it did not match the requestMatcher org.springframework.se[email protected]7ffcc692 
2016-07-29 15:06:38 DEBUG [] org.springframework.security.web.context.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper.saveContext:352 (http-nio-8080-exec-2) - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. 
2016-07-29 15:06:38 DEBUG [] org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter:119 (http-nio-8080-exec-2) - SecurityContextHolder now cleared, as request processing completed 

UPDATE 2

Ich habe mehr den Prozess untersucht:

  • Die outbound-gateway (application1) sendet eine Nachricht an einen entfernten Endpunkt (Application2) mit dem "sendAndReceive()"; Die Nachricht enthält den Header "application/octet-stream", weil ich ein Byte-Array als Payload übergebe.
  • der entfernte Endpunkt (Application2) ein inbound-gateway mit einem Service-Aktivator
  • Die service activator (Application2) verarbeitet die Anforderung und liefert einen String.
  • Die (Anwendung1) versuchen, die Antwort mit der contentType der Nachricht, die an die "application2" (application/octet-stream) gesendet wurde, zu lesen.

Ich denke, dass das Problem hier ist: Ich habe eine Nachricht mit dem outbound-gateway mit dem Header application/octet-stream, aber ich habe erhalten einen String (mit application/json) als Antwort gesendet, keine Anwendung/Oktett -stream.

Dies sind die Informationen von der inputMessage Variable von der readInternal Methode der Klasse verwendet org.springframework.http.converter.StringHttpMessageConverter:

inputMessage MessageBodyClientHttpResponseWrapper (id=111) 
    pushbackInputStream PushbackInputStream (id=146) 
    response SimpleClientHttpResponse (id=121) 
     connection HttpURLConnection (id=125) 
     headers HttpHeaders (id=182) {Server=[Apache-Coyote/1.1], Pragma=[no-cache], Accept=[application/json], host=[localhost:8080], connection=[keep-alive], Cache-Control=[no-cache], user-agent=[Java/1.8.0_65], X-XSS-Protection=[1; mode=block], X-Frame-Options=[DENY], X-Content-Type-Options=[nosniff], Content-Type=[application/octet-stream], Content-Length=[968], Date=[Mon, 01 Aug 2016 12:52:47 GMT]} 
     responseStream HttpURLConnection$HttpInputStream (id=180) 

Die Content-Type ist application/octet-stream, aber darf dies nur in der Meldung verwiesen werden ausgehende, nicht für die eingehende Nachricht in Antwort erhalten!

Es gibt eine Möglichkeit, den Inhaltstyp der Antwort zu konfigurieren, die einen Outbound-Gateway empfangen kann?

Mein Service Aktivator der Application2 Prozess die Antwort auf diese Weise:

public class ChannelService { 

    public String manage(Message<?> message) throws Throwable{   
     ServletRequestAttributes sra = (ServletRequestAttributes) message.getHeaders().get("requestAttributes"); 
     HttpServletRequest request = sra.getRequest(); 
     System.out.println(request.getRequestURL()); 

     Object payload = message.getPayload(); 

     return "{ \"test\" : \"test\"}"; 
    } 

} 

UPDATE 3

Das Problem ist die InputStream durch die HttpInputMessage mit inputMessage.getBody() erhalten. Wenn dieser InputStream mit der Methode "read()" verarbeitet wird, ist das Ergebnis von "read()" für 20 Sekunden niemals -1. Ich habe eine benutzerdefinierte StringHttpMessageConverter implementiert:

@Override 
protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) 
     throws IOException, HttpMessageNotReadableException { 
    System.out.println("ResponseStringHttpMessageConverter.readInternal()"); 

    int content; 
    long start = System.currentTimeMillis(); 
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    while ((content = inputMessage.getBody().read()) != -1){ 
     System.out.println(content); 
     baos.write(content); 
    } 
    long end = System.currentTimeMillis(); 
    System.out.println("TIME READING: "+(end-start)); 
    String response = baos.toString(); 
    System.out.println(response); 
    return response; 
} 

Die benutzerdefinierten readInternal Methode druckt jedes einzelne Zeichen ASCII-Code, nach dem letzten Zeichen stoppt er den Druck und beim Neustart druckt:

TIME READING: 20164 

So nehme ich an dass im InputStream der Nachricht etwas nicht stimmt, weil die read() Methode vor 20 Sekunden nicht "-1" zurückgibt.

UPDATE 4

ich das Problem in der Converter Seite gelöst, aber es ist das gleiche Problem in der Schließung der Reaktion in der doExecute() Methode der org.springframework.web.client.RestTemplate Klasse.

Verfahren versuchen, die Antwort (eine Instanz von org.springframework.http.client.SimpleClientHttpResponse) zu schließen:

@Override 
public void close() { 
    if (this.responseStream != null) { 
     try { 
      StreamUtils.drain(this.responseStream); // <- again the same problem 
      this.responseStream.close(); 
     } 
     catch (IOException e) { } 
    } 
} 

Dies ist die "drain" Implementierung von org.springframework.util.StreamUtils Klasse:

public static int drain(InputStream in) throws IOException { 
    Assert.notNull(in, "No InputStream specified"); 
    byte[] buffer = new byte[BUFFER_SIZE]; 
    int bytesRead = -1; 
    int byteCount = 0; 
    while ((bytesRead = in.read(buffer)) != -1) { // it blocks here !!! 
     byteCount += bytesRead; 
    } 
    return byteCount; 
} 

In meinem Konverter löste ich das Problem ein mit Puffer mit der erwarteten verfügbaren Puffergröße:

byte[] buffer = new byte[in.available()]; 
in.read(buffer); 
baos.write(buffer); // (a simple ByteArrayOutputStream) 

Ich bin definitiv davon überzeugt, dass etwas an meiner Konfiguration des Outbound-Gateway/Inbound-Gateway/Service-Aktivators nicht stimmt, weil die Antwort kurz vor dem Lesen des -1-Byte von EOS einen Block verursacht. Sonst würde es einen Fehler geben, und ich glaube nicht an diese Option.

+0

Bitte teilen Sie Debug-Logs für 'org.springframework.integration', um die wahre Reise Ihrer" langsamen "Nachricht zu sehen. –

+0

Ich habe die Frage mit den Protokollen von org.springframework.integration –

Antwort

1

Nach Sie Protokolle:

2016-07-29 15:06:18 DEBUG [] org.springframework.web.client.HttpMessageConverterExtractor.extractData:101 (executor-1) - Reading [java.lang.String] as "application/octet-stream" using [[email protected]97e277] 
2016-07-29 15:06:38 DEBUG [] org.springframework.integration.http.support.DefaultHttpHeaderMapper.toHeaders:436 (executor-1) - inboundHeaderNames=[*] 

Es sieht aus wie Sie sehr große Resonanz haben, die für die Umwandlung zu String diese Zeit in Anspruch nimmt.

Siehe HttpMessageConverterExtractor.extractData(ClientHttpResponse response).

Es gibt nichts, was Spring Integration für tut.

Wäre besser, wenn Sie das debuggen StringHttpMessageConverter.readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) und schauen Sie, was ist Ihre echte Antwort.

+0

aktualisiert Danke für Ihre Antwort. Ich werde versuchen, dieses Problem zu untersuchen. –

+0

Zu Ihrer Information friert die Anwendung in der 'java.util.concurrent.ThreadPoolExecutor.class' im' while'-Zyklus der 'runWorker'-Methode ein. Ich habe versucht, den Wert des 'pool-size' Attributes der' 'Tags zu erhöhen, aber es hat das Problem nicht gelöst. Es ist genau in diesem Punkt, dass die Anwendung für 20 Sekunden wartet. –

+0

Es ist richtig, dass Ihr Thread Executor blockiert ist und nichts anderes kann es verwenden, aber das ist kein Grund. Sie haben genau ein Problem mit der Antwort. –

0

Ich habe das Problem gelöst, aber um es zu lösen, musste ich eine benutzerdefinierteRestTemplate erstellen.

In meiner benutzerdefinierten RestTemplate habe ich überschreiben die Methode "doExecute()".

Die Implementierung ist identisch mit der ursprünglichen RestTemplate.doExecute, außer dem finally-Block.

In diesem Block, ich rufe nicht "response.close()", aber ich habe eine benutzerdefinierte Methode erstellt, die zum Schließen der Antwort Client bereitstellt.

Dies ist der Brauch RestTemplate Implementierung:

public class AppRestTemplate extends RestTemplate { 

    @Override 
    protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, 
      ResponseExtractor<T> responseExtractor) throws RestClientException { 

     Assert.notNull(url, "'url' must not be null"); 
     Assert.notNull(method, "'method' must not be null"); 
     ClientHttpResponse response = null; 
     try { 
      ClientHttpRequest request = createRequest(url, method); 
      if (requestCallback != null) { 
       requestCallback.doWithRequest(request); 
      } 
      response = request.execute(); 
      handleResponse(url, method, response); 
      if (responseExtractor != null) { 
       return responseExtractor.extractData(response); 
      } 
      else { 
       return null; 
      } 
     } 
     catch (IOException ex) { 
      String resource = url.toString(); 
      String query = url.getRawQuery(); 
      resource = (query != null ? resource.substring(0, resource.indexOf(query) - 1) : resource); 
      throw new ResourceAccessException("I/O error on " + method.name() + 
        " request for \"" + resource + "\": " + ex.getMessage(), ex); 
     } 
     finally { 
      if (response != null) { 
       // response.close(); // <- commented; this is the cause of the slow process 
       close(response); 
      } 
     } 
    } 

    /** This method only provides to close the stream, without draining it */ 
    private void close(ClientHttpResponse response) { 
     try { 
      if (response.getBody()!=null){ 
       response.getBody().close(); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

} 

Offensichtlich musste ich die neue RestTemplate im Anwendungskontext Konfigurationsregister:

<bean id="restTemplate" name="restTemplate" class="mypackage.integration.AppRestTemplate" autowire-candidate="true"> 
    <property name="messageConverters"> 
     <list> 
      <bean class="mypackage.integration.ResponseStringHttpMessageConverter" /> 
      <!-- ... other converters --> 
     </list> 
    </property> 
</bean> 

Last but not least, hatte ich auch zu implementieren Sie einen benutzerdefinierten Konverter, dass ersetzen das Original StringHtppMessageConverter (in meinem Fall ResponseStringHttpMessageConverter):

public class ResponseStringHttpMessageConverter extends AbstractHttpMessageConverter<String> { 

    private Logger log = LoggerFactory.getLogger(ResponseStringHttpMessageConverter.class); 

    public ResponseStringHttpMessageConverter() { 
     super(new MediaType("application","json")); 
    } 

    @Override 
    protected boolean supports(Class<?> clazz) { 
     if (clazz == String.class) 
      return true; 
     else return false; 
    } 

    @Override 
    protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) 
      throws IOException, HttpMessageNotReadableException {  
     int content; 
     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
     byte[] buffer = new byte [inputMessage.getBody().available()]; 
     inputMessage.getBody().read(buffer); 
     baos.write(buffer); 
     String response = baos.toString(); 
     baos.close(); 
     return response; 
    } 

    @Override 
    protected void writeInternal(String content, HttpOutputMessage outputMessage) 
      throws IOException, HttpMessageNotWritableException { 

    } 

} 

Mit dieser Implementierung die Anforderungen leiden nicht mehr von dem langsamen Problem.

P.S. Ich verwende Spring 4.3.1.RELEASE und Spring Integration 4.3.0.RELEASE.

Ich denke, dass dies keine definitive Lösung ist, weil ich zwei Framework-Komponenten anpassen musste.

+0

Diese Lösung funktioniert nicht ordnungsgemäß, da 'inputMessage.getBody(). Available()' niemals die korrekte Anzahl an Bytes zurückgibt, die im InputStream enthalten sind. Es ist jedoch ein Problem von InputStream. –