当使用RestTemplate与外部服务对话时,我不止一次在我们的应用中看到OutOfMemory错误,因为服务的数据流有几千兆的数据(由于他们那边的一个不好的实现,在错误的情况下,他们在数组的每一个元素中都发回了很大的堆栈痕迹,通常包含几千个)。它的结局是大约6GB的数据,在我们的应用程序中被jackson序列化,并且完全爆炸了jvm的Xmx。
我看了一下,但似乎没有任何方法可以防止这种情况,即当流式响应超过给定大小时,中止请求。
有什么办法可以解决这个问题吗?我们使用的是apache的httpcomponents httpclient 4.5.5,但任何其他的底层实现都可以接受。
除了RestTemplate,也欢迎Spring的反应式WebClient的解决方案。
这必须在底层的HTTP客户端库中执行(spring支持不同的库,比如JDK客户端,apache客户端,okHTTP...)。
在这里你说的是apache-httpcomponent,你检查过这个吗?HttpEntity.getContent()
实际上,它返回的是一个输入流,你可以自己读取,并确定何时已经超过了大小。
https:/hc.apache.orghttpcomponents-core-4.4.xhttpcoreapidocsorgapachehttpHttpEntity.html。
记录一下,这是最终的解决方案。问题是要加载一个可能非常大的对象列表,使用分页(通过弹性搜索滚动api)。
ResponseExtractor<Car[]> responseExtractor = responseEntity -> {
long pageContentLengthInBytes = responseEntity.getHeaders().getContentLength();
long presumableFreeMemoryInBytes = this.getAvailableFreeMemoryAmount();
if (presumableFreeMemoryInBytes - TWENTY_MEGABYTES < pageContentLengthInBytes) {
log.error("Not enough memory to store the page ({} avaiable, content-length={}, trashing it", presumableFreeMemoryInBytes, pageContentLengthInBytes);
responseEntity.close();
return null;
}
return objectMapper.readValue(responseEntity.getBody(), Car[].class);
};
Car[] responseEntities = this.restTemplate.execute(uri, HttpMethod.GET, null, responseExtractor);
/**
* Returns the current amount of memory which may be allocated until an out-of-memory error occurs.
* see https://stackoverflow.com/a/12807848/8836232
*/
private long getAvailableFreeMemoryAmount() {
long allocatedMemory = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
return Runtime.getRuntime().maxMemory() - allocatedMemory;
}