繁体   English   中英

RestTemplate postForEntity遇到java.io.IOException:过早的EOF错误

[英]RestTemplate postForEntity experiencing java.io.IOException: Premature EOF error

希望对我的一个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();
}

ResponseUtility

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

CampaignData服务

@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。 服务器日志正常(返回成功响应,要么记录成功,要么记录成功)

问题是断断续续的。 发送批量请求时随机发生。

提前致谢! :)

基于服务器日志,服务器正在尝试保存一些记录及其失败(由于唯一键冲突)。

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

看起来服务器无法正常处理该异常,并且整个流程中断,从而导致HTTP-500响应代码(可能)响应为空。

您可以执行以下两项操作:

  1. 优雅地处理异常。
  2. 验证为什么会破坏唯一密钥,并在可能的情况下进行修复。

对于任何可能会遇到这种情况的人。 问题是由春季启动eureka引起的。 大规模传递ResponseEntity响应(批量处理)时似乎存在一个错误,这会导致响应状态格式错误。

当前的解决方法是从ResponseEntity切换到对象主体。

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

A --> B --> C

B A的响应中发生Premature EOF 。所有指示都是成功的HTTP响应(200),但是使用调试器检查响应的主体后发现,它在序列化DTO数据的中间具有换行符,而不是在我期望的结尾处:

在此处输入图片说明

(请注意, 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过滤器记录每个服务的响应。 该过滤器必须复制响应流,以避免其他与已消耗主体有关的过滤器出现异常。

我注意到的是,当我在启用此过滤器的情况下运行时, 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() 这迫使服务C中的整个响应主体在返回服务A前被消耗掉。进行此更改之后,我的Premature EOF错误没有返回。

我将RestTemplate与默认的http客户端工厂一起使用时遇到相同的问题。我发现它在捕获数据包时在标头中丢失了'Accept-Encoding:gzip'。最后我将其替换为默认的http客户端工厂与apache的http客户端工厂,像这样:

HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create().build());
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM