【问题标题】:Why does RestTemplate consume excessive amounts of memory?为什么 RestTemplate 会消耗过多的内存?
【发布时间】:2020-07-03 11:19:29
【问题描述】:

问题

为什么 Spring 的 RestTemplate 在发送文件时会使用过多的堆(尤其是G1 Old Generation)。

上下文

我们观察到RestTemplate 在通过POST 请求发送文件时会消耗过多的内存。我们使用 Spring 的 WebClient 作为比较,它的行为完全正常。

我们创建了一个包含完整代码的demo project on github。重要的部分是以下sn-ps:

private void sendFileAsOctetStream(File file) {
    final RequestEntity<FileSystemResource> request = RequestEntity.post(URI.create("http://localhost:8080/file"))
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(new FileSystemResource(file));
    restTemplate.exchange(request, void.class);
}

private void sendFileAsOctetStream(File file) {
    webClient.post()
            .uri("/file")
            .body(BodyInserters.fromResource(new FileSystemResource(file)))
            .exchange()
            .block();
}

我们观察了jconsole 的内存使用情况,当发送具有两种实现的 550MB 文件时(左侧是 WebClient,右侧是 RestTemplateWebClient 占用几兆字节,而 RestTemplate 需要 2.7技嘉:

  1. 用于清理老年代的初始手动 GC
  2. 请求
  3. 手动 GC(仅适用于 RestTemplate

【问题讨论】:

    标签: java spring spring-mvc resttemplate


    【解决方案1】:

    这是由于默认的RestTemplate 简单地使用未配置的SimpleClientHttpRequestFactory 来创建请求。

    提到的请求工厂有一个标志bufferRequestBody,默认设置为true,这会导致发送大请求时内存消耗非常高。

    来自SimpleClientHttpRequestFactory#setBufferRequestBody()的javadoc:

    指示此请求工厂是否应在内部缓冲请求正文。 默认为真。 当通过 POST 或 PUT 发送大量数据时,建议将此属性更改为 false,以免内存不足。这将导致 ClientHttpRequest 直接流式传输到底层 HttpURLConnection(如果事先知道 Content-Length),或者将使用“分块传输编码”(如果事先不知道 Content-Length)。

    您可以在使用其他重载构造函数之一创建RestTemplate 时提供自己的请求工厂,并将请求工厂中提到的标志设置为false

    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory rf = new SimpleClientHttpRequestFactory();
        rf.setBufferRequestBody(false);
        return new RestTemplate(rf);
    }
    

    【讨论】:

    • 救命!非常感谢!
    猜你喜欢
    • 2019-09-30
    • 2018-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-09
    相关资源
    最近更新 更多