java - RestTemplate postForEntity 遇到 java.io.IOException : Premature EOF error

标签 java spring maven spring-boot

对于我的一个 Maven 项目中的问题,我们将不胜感激。

Exception in thread "main" org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://test-services.domain.ph/campaign/": Premature EOF; nested exception is java.io.IOException: Premature EOF
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:666)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:407)
at homecredit.ph.CampaignConnector.call(CampaignConnector.java:46)
Caused by: java.io.IOException: Premature EOF
at sun.net.www.http.ChunkedInputStream.readAheadBlocking(ChunkedInputStream.java:565)
at sun.net.www.http.ChunkedInputStream.readAhead(ChunkedInputStream.java:609)
at sun.net.www.http.ChunkedInputStream.read(ChunkedInputStream.java:696)
at java.io.FilterInputStream.read(FilterInputStream.java:133)

来源:

ResponseEntity<ApiResponse> response = restTemplate.postForEntity(url, entity, ApiResponse.class);

目的地:

@RequestMapping(value="/campaign", method = RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ApiResponse> insertCampaignRecord(
        @Valid @RequestBody CampaignRecordInsertRequest campaignRecordInsertRequest){
    logInfo("Incoming insert request. " + DescriptorUtility.convertToString(campaignRecordInsertRequest));
    campaignDataService.insertNewRecord(CampaignRecordConverter.convertToCampaignRecord(campaignRecordInsertRequest));
    return ResponseUtility.defaultResponse();
}

响应实用程序

public static ResponseEntity<ApiResponse> defaultResponse(){
    ApiResponse apiResponse = new ApiResponse();
    apiResponse.setTimestamp(DateUtility.currentDateString());
    apiResponse.setMessage(ResponseMessages.SUCCESS);
    return new ResponseEntity<>(apiResponse, HttpStatus.OK);
}

Activity 数据服务

@Async("AsyncExecutor")
public void insertNewRecord(CampaignRecord campaignRecord) {
    try {
        campaignRecordRepository.save(campaignRecord);
    } catch (Exception e) {
        logError(e);
    }
}

服务器日志

2017-09-11 11:11:11  INFO 18383 [http-nio-8773-exec-10] [CampaignRecordController] - Incoming insert request. {"dateCampaign":1504656000000,"cuid":...
2017-09-11 11:11:11  WARN 18383 [http-nio-8773-exec-10] [SqlExceptionHelper] - SQL Error: 1062, SQLState: 23000
2017-09-11 11:11:11 ERROR 18383 [http-nio-8773-exec-10] [SqlExceptionHelper] - Duplicate entry 'CMP_CLX##1208637#20170906' for key 'UNIQUE_KEY'
2017-09-11 11:11:11 ERROR 18383 [http-nio-8773-exec-10] [CampaignDataService] - could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
2017-09-11 11:11:11 ERROR 18383 [http-nio-8773-exec-10] [CampaignDataService] - could not execute statement
PS。服务器日志正常(记录保存成功与否返回成功响应)

问题是间歇性的。发送批量请求时随机发生。

提前致谢! :)

最佳答案

我在 Spring Boot 2.1 中遇到了同样的问题。就我而言,我有 3 个应用程序(称它们为 A、B 和 C),其中 B 实际上只是 A 和 C 之间的代理:

A --> B --> C

从 B A 的响应中发生了过早 EOF。所有指示都是成功的 HTTP 响应 (200),但检查使用调试器的响应正文显示它在序列化 DTO 数据的中间有一个换行符,而不是在我预期的末尾:

enter image description here

(注意id字段后面的返回字符,并且缺少任何内容长度;忽略末尾的不可读框,它们是未初始化/使用的字节数组的一部分)

就我而言,服务 B 既是服务器又是客户端。代码看起来像这样:

public ResponseEntity<String> handle(String request, HttpHeaders headers) {

    // Do some validation and then call Service C, and pass the response
    // back to Service A

    return restTemplate.postForEntity(
        urlForServiceC,
        new HttpEntity<>(request, headers),
        String.class);
}

我没有深入研究 RestTemplate 或其消息转换器的内部,但让我意识到响应缓冲可能存在问题的是我使用了 Spring filter记录每个服务的响应。此过滤器必须复制响应流,以避免与已使用的正文相关的其他过滤器出现异常。

我注意到,当我启用此过滤器运行时,Premature EOF 异常消失了。当我禁用它时,异常又回来了。有关复制响应流的一些操作已经解决了 Premature EOF 错误。

这促使我在服务 B 中尝试以下操作:

public ResponseEntity<String> handle(String request, HttpHeaders headers) {

    // Do some validation and then call Service C, and pass the response
    // back to Service A

    String response = restTemplate.postForEntity(
        urlForServiceC,
        new HttpEntity<>(request, headers),
        String.class).getBody();

    return ResponseEntity.ok(response);
}

微妙的变化是我首先将响应保存到本地变量,这需要我调用ResponseEntity.getBody()。这会强制在返回服务 A 之前消耗来自服务 C 的整个响应正文。进行此更改后,我的 Premature EOF 错误尚未返回。

关于java - RestTemplate postForEntity 遇到 java.io.IOException : Premature EOF error,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46147813/

相关文章:

java - 是否有 "embedded DBMS"来支持同一数据库文件上的多个编写器应用程序(进程)?

java - 为什么我已经安装了 eclipse 的 e(fx)clipse 却无法导入 javafx

java - 将 Firestore 数据加载到 json 中

Java - Sonar - 不应使用循环复制数组

java - 从 Spring Controller 返回 json View 的步骤

java - 如何在同一个 spring-WS 项目中为不同的服务定义单独的 wsdl?

maven - IntelliJ:需要导入Maven项目:导入更改启用自动导入

java - 成功登录 google oauth2 时出现重定向错误

java - spring 映射的 servlet 未在 jetty-maven-plugin 中调用

java - 在sts嵌入的apache tomcat服务器无法启动