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.
Bitte teilen Sie Debug-Logs für 'org.springframework.integration', um die wahre Reise Ihrer" langsamen "Nachricht zu sehen. –
Ich habe die Frage mit den Protokollen von org.springframework.integration –