简体   繁体   English

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

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

Would appreciate any help regarding my issue on one of my maven projects. 希望对我的一个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)

Origin: 起源:

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

Destination: 目的地:

@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 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 Service CampaignData服务

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

Server Log 服务器日志

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. PS。 Server logs is normal(return a successful response either record successfully saved or not) 服务器日志正常(返回成功响应,要么记录成功,要么记录成功)

Issue is intermittent. 问题是断断续续的。 Occurs randomly when sending bulk requests. 发送批量请求时随机发生。

Thanks in advance! 提前致谢! :) :)

Based on the server logs seems like, the server is trying save some record and its failing (due to Unique key violation). 基于服务器日志,服务器正在尝试保存一些记录及其失败(由于唯一键冲突)。

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

Looks like the server is not able to handle the exception gracefully and the whole flow breaks, causing a HTTP-500 response code (probably) with an empty response. 看起来服务器无法正常处理该异常,并且整个流程中断,从而导致HTTP-500响应代码(可能)响应为空。

Two actions you can take: 您可以执行以下两项操作:

  1. Handle the exception gracefully. 优雅地处理异常。
  2. Verify why unique key is getting violated and fix that if possible. 验证为什么会破坏唯一密钥,并在可能的情况下进行修复。

For anyone who might be experiencing this. 对于任何可能会遇到这种情况的人。 Issue was caused by spring boot eureka. 问题是由春季启动eureka引起的。 Seems like there is a bug when it comes to passing ResponseEntity response in a massive scale (bulk processing) that causes the response status to be malformed. 大规模传递ResponseEntity响应(批量处理)时似乎存在一个错误,这会导致响应状态格式错误。

Current workaround is to switch from ResponseEntity to the object body instead. 当前的解决方法是从ResponseEntity切换到对象主体。

I had the same problem in Spring Boot 2.1. 我在Spring Boot 2.1中遇到了同样的问题。 In my case I had 3 apps (call them A, B, and C) where B was really just a proxy between A and C: 就我而言,我有3个应用程序(分别称为A,B和C),其中B实际上只是A和C之间的代理:

A --> B --> C

The Premature EOF was occurring on the response from B to A. All indications were a successful HTTP response (200), but inspecting the body of the response using a debugger revealed it had a new line character in the middle of the serialized DTO data, instead of at the end where I expected it: B A的响应中发生Premature EOF 。所有指示都是成功的HTTP响应(200),但是使用调试器检查响应的主体后发现,它在序列化DTO数据的中间具有换行符,而不是在我期望的结尾处:

在此处输入图片说明

(Notice the return character after the id field, and lack of any content length; ignore the unreadable boxes at the end, they're part of the byte array that are not initialized/used) (请注意, id字段后面的返回字符,并且缺少任何内容长度;请忽略末尾的不可读框,它们是未初始化/未使用的字节数组的一部分)

In my case, Service B is both a server and a client. 就我而言,服务B既是服务器又是客户端。 The code looked something like this: 代码看起来像这样:

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);
}

I didn't dive too far into the guts of RestTemplate or its message converters, but what tipped me off that there might be an issue with the response buffering is that I was using a Spring filter to log the responses of each service. 我并没有深入研究RestTemplate或其消息转换器,但是告诉我响应缓冲可能存在问题的原因是我正在使用Spring过滤器记录每个服务的响应。 This filter has to copy the response stream to avoid exceptions from other filters related to the body already being consumed. 该过滤器必须复制响应流,以避免其他与已消耗主体有关的过滤器出现异常。

What I noticed is that when I ran with this filter enabled , the Premature EOF exceptions went away. 我注意到的是,当我在启用此过滤器的情况下运行时, Premature EOF异常消失了。 And when I disabled it, the exceptions came back. 当我禁用它时,异常又回来了。 Something about copying the response stream had solved the Premature EOF errors. 关于复制响应流的某种方法已解决了Premature EOF错误。

This led me to try the following in Service B: 这导致我尝试在服务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);
}

The subtle change is that I'm saving the response first to a local variable, which requires me to call ResponseEntity.getBody() . 细微的变化是,我先将响应保存到本地变量,这需要我调用ResponseEntity.getBody() This forces the entire response body from Service C to be consumed before returning to Service A. After making this change my Premature EOF errors have not returned. 这迫使服务C中的整个响应主体在返回服务A前被消耗掉。进行此更改之后,我的Premature EOF错误没有返回。

I have the same problem when i use RestTemplate with default http client factory.I found it missing 'Accept-Encoding:gzip' in the headers when I capture packets.Finally i get it by replace the default http client factory with apache's http client factory,like this: 我将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