You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RestTemplate发起GET请求后挂起问题排查求助

排查RestTemplate反序列化POJO时响应缓慢的问题

从你描述的现象来看,核心矛盾点非常明确:用String接收响应秒级返回,用自定义POJO就阻塞30-60秒,结合curl的错误提示transfer closed with 79 bytes remaining to read,大概率是HTTP响应的Content-Length与实际响应体大小不匹配,导致客户端的反序列化组件一直在等待“缺失”的字节,最终触发超时。下面给你一步步的排查和解决建议:

1. 先验证Content-Length的准确性

这是最关键的第一步,直接用curl -v http://friend:5000/users请求第三方服务,重点看响应头里的Content-Length值;然后把响应体保存到文件(比如curl http://friend:5000/users > response.json),用wc -c response.json计算实际响应体的字节数。

如果两者数值不一致,问题根源就在第三方服务:它返回了错误的Content-Length,导致RestTemplate的Jackson消息转换器在读取响应流时,一直等待到超时才结束。这种情况下,你需要联系服务方修复这个HTTP头的问题。

2. 绕过RestTemplate自动反序列化,手动处理

如果暂时没法让服务方修复,可以先绕过RestTemplate的自动反序列化流程,先把响应读成String,再手动转成POJO,这样能避免流阻塞的问题:

@GetMapping("/todos/{toDoNoteId}/users")
public ResponseEntity getNotesUsersTest(@PathVariable int toDoNoteId) {
    // 省略前置校验逻辑
    RestTemplate restTemplate = new RestTemplate();
    final String uri = "http://friend:5000/users";
    try {
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<>("parameters", requestHeaders);
        
        // 先获取String响应
        ResponseEntity<String> stringResult = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
        // 手动反序列化
        ObjectMapper objectMapper = new ObjectMapper();
        ArrayResponsePojo pojo = objectMapper.readValue(stringResult.getBody(), ArrayResponsePojo.class);
        
        return ResponseEntity.ok(pojo);
    } catch (HttpClientErrorException ex) {
        // 异常处理逻辑
    } catch (JsonProcessingException e) {
        // 反序列化异常处理
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("反序列化失败");
    }
}

这种方式如果能快速返回,就进一步验证了是自动反序列化时的流读取阻塞问题。

3. 调整RestTemplate的消息转换器配置

默认的MappingJackson2HttpMessageConverter可能在处理响应流时,因为Content-Length不匹配导致阻塞。你可以尝试修改配置,关闭缓冲:

// 配置请求工厂关闭缓冲
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setBufferRequestBody(false);
RestTemplate restTemplate = new RestTemplate(factory);

// 或者自定义Jackson消息转换器,关闭缓冲
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setBufferRequestBody(false);
restTemplate.getMessageConverters().add(0, converter);

4. 深挖线程挂起的具体细节

你提到线程长时间挂起在ThreadPoolExecutor.afterExecute或LockSupport.park,可以用jstack <你的应用PID>导出线程栈,找到挂起的线程查看调用栈:

  • 如果停在InputStream.read()相关方法,那100%是流读取阻塞,等待服务端发送剩余字节;
  • 如果停在Jackson的反序列化方法里,可能是POJO的字段映射有隐藏问题(比如有未知字段导致Jackson反复尝试解析,但这种情况一般会抛异常而非阻塞)。

5. 检查服务端的传输编码合规性

用curl -v看响应头,如果同时存在Transfer-Encoding: chunked和Content-Length,这是违反HTTP规范的(Chunked编码下不应返回Content-Length),会导致客户端解析混乱出现阻塞。这种情况同样需要服务方修复。

6. 排除POJO反序列化的隐藏问题

虽然你说POJO有正确的getter、setter和无参构造,但可以尝试给POJO加上@JsonIgnoreProperties(ignoreUnknown = true),避免Jackson因为响应体里有未知字段而出现异常(极端场景下可能导致阻塞):

@JsonIgnoreProperties(ignoreUnknown = true)
public class ArrayResponsePojo {
    private User[] data;
    private String message;
    // getter、setter及无参构造方法
}

@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
    private String email;
    private String firstName;
    private String lastName;
    // getter、setter及无参构造方法
}

补充说明

你提到浏览器请求快而Postman/curl慢,是因为浏览器的HTTP客户端实现比较“宽松”,会在流关闭时就停止读取;而Postman、curl以及RestTemplate的默认实现会严格按照Content-Length来读取,所以会一直等待到超时。

内容的提问来源于stack exchange,提问作者Svajunas Kavaliauskas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:21:44