我有一个带有Spring 4.0.3.RELEASE的Java 8应用程序。从这个应用程序,我将请求异步发送到10个不同的Rest服务。
更新:这10个不同的Rest服务是一个单体应用程序上的10个不同的端点。
代码工作正常,但有时会请求挂起并抛出SocketTimeoutException。如果我在RestTemplate对象上设置了一些超时,则它将在该特定超时之后引发异常。但是,如果我不设置超时,则请求会挂起,并在一段时间后引发相同的异常。我的应用程序调用的服务是使用Node.js编写的,该应用程序的错误如下:
ERROR CATCHED: ClientError [BadRequestError]: request aborted
at abortIncoming (_http_server.js:492:9)
message: 'request aborted',type: 'request.aborted'
据我所知,此错误是由于在Node.js应用程序读取请求之前请求被取消而发生的。例如,当您单击Postman的“发送”按钮并立即单击“取消”按钮时。但是,当我一个接一个地同步发送请求时,除了时间没有问题。我不想因性能问题而同步调用这些服务。
Java方面的异常是:
org.springframework.web.client.ResourceaccessException: I/O error on POST request for "SOMEURL":Read time out after 1000 millis; nested exception is java.net.SocketTimeoutException: Read time out after 1000 millis
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:557)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:330)
at tr.com.ingbank.service.impl.IntentServiceImpl.getIntentResponse(IntentServiceImpl.java:231)
at tr.com.ingbank.service.impl.IntentServiceImpl.access$000(IntentServiceImpl.java:51)
at tr.com.ingbank.service.impl.IntentServiceImpl$1.get(IntentServiceImpl.java:105)
at tr.com.ingbank.service.impl.IntentServiceImpl$1.get(IntentServiceImpl.java:98)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.net.SocketTimeoutException: Read time out after 1000 millis
at weblogic.socket.NIOSocketMuxer$NIOInputStream.readInternal(NIOSocketMuxer.java:855)
at weblogic.socket.NIOSocketMuxer$NIOInputStream.read(NIOSocketMuxer.java:788)
at weblogic.socket.NIOSocketMuxer$NIOInputStream.read(NIOSocketMuxer.java:771)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
at weblogic.net.http.MessageHeader.isHTTP(MessageHeader.java:310)
at weblogic.net.http.MessageHeader.parseHeader(MessageHeader.java:232)
at weblogic.net.http.HttpClient.parseHTTP(HttpClient.java:549)
at weblogic.net.http.HttpURLConnection.getInputStream(HttpURLConnection.java:639)
at weblogic.net.http.SOAPHttpURLConnection.getInputStream(SOAPHttpURLConnection.java:37)
at weblogic.net.http.HttpURLConnection.getResponseCode(HttpURLConnection.java:1445)
at org.springframework.http.client.SimpleclientHttpResponse.getRawStatusCode(SimpleclientHttpResponse.java:48)
at org.springframework.http.client.AbstractClientHttpResponse.getStatusCode(AbstractClientHttpResponse.java:33)
at org.springframework.web.client.DefaultResponseErrorHandler.getHttpStatusCode(DefaultResponseErrorHandler.java:56)
at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:50)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:542)
... 8 more
我的异步REST调用代码如下:
CompletableFuture<IntentResponse> future = CompletableFuture.supplyAsync(new Supplier<IntentResponse>() {
@Override
public IntentResponse get() {
System.out.println("Calling service asynchronously with botName=" + finalBotName);
try {
return getIntentResponse(finalIntentRequest,finalBotName);
} catch (Exception e) {
e.printStackTrace();
// insert error log
return new IntentResponse();
}
}
});
futures.add(future);
此代码块位于for循环内,这是准备所有请求的方式。准备好所有请求之后,我调用 get()方法,该方法将运行所有其余调用,并且 allOf()方法允许我的应用程序等待所有响应。
CompletableFuture<IntentResponse>[] responseFuturesArray = (CompletableFuture<IntentResponse>[]) new CompletableFuture[futures.size()];
futures.toArray(responseFuturesArray);
CompletableFuture<Void> result = CompletableFuture.allOf(responseFuturesArray);
result.get();
for(CompletableFuture<IntentResponse> future : futures){
intentResponses.add(future.get());
}
异步调用的方法是使用Springframework中的RestTemplate类进行的简单REST调用。如果出现问题,我也可以使用此方法:
private IntentResponse getIntentResponse(IntentRequest intentRequest,String botName) throws Exception {
String url = appConfigService.getappConfigValueByType(AppConstants.INTERNAL_INTENT_ENGINE_URL);
String token = appConfigService.getappConfigValueByType(botName);
url = url + botName;
String sessionIdWithBotName = intentRequest.getUserId();
String sessionId = sessionIdWithBotName.split("_").length > 1 ? sessionIdWithBotName.split("_")[1] : sessionIdWithBotName;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setaccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.add("Cbot-Token",token);
long start = System.currentTimeMillis();
HttpEntity<IntentRequest> request = new HttpEntity<IntentRequest>(intentRequest,headers);
IntentResponse response = restTemplate.postForObject(url,request,IntentResponse.class);
response.setBotName(botName);
long elapsedTime = System.currentTimeMillis() - start;
// insert log
return response;
}
我已经处理了2个星期的问题,没有尝试过。我试图编写使用HttpUrlConnection,ExecutorService-newCachedThreadPool,newFixedThreadPool等发送请求的方法,我知道它们之间的区别,但是它们都有相同的错误,并且这些错误没有模式(与请求无关,不是与时间或请求之间的时间间隔有关。)。所有请求都已成功发送并成功接收到响应,或者所有请求都挂起,超时或被调用。任何帮助将不胜感激。