Spring RestTemplate 不支持流式传输

问题描述 投票:0回答:1

关于如何使 RestTemplate 流式传输请求的问题,有很多答案。

为什么RestTemplate会消耗过多内存?

使用 RestTemplate 的 POST 输入流

通过在 requestfactory 上将缓冲设置为 false 看起来很简单。好吧,经过 3 天的调试,我认为它实际上根本不支持流媒体。如果有人可以解释为什么以下解释是错误的,那将非常有帮助。

我使用的是springframework 5.3.19。也许它仅在该版本中不起作用?确实,我应该使用 WebClient,但一开始我想了解为什么我是这个星球上唯一没有可行解决方案的人......

那么使用 spring RestTemplate 时会发生什么。这是一些伪代码来解释高层发生的事情:

Request request = create(args); // creates the intercepting request
messageConverters.prepare(request); // uses the intercepting request!!
request.execute(); // create the actual request as underlying delegate

根据定义,RestTemplate 是一个 InterceptingHttpAccessor,这意味着它通过拥有自己的包装 requestFactory (InterceptingClientHttpRequestFactory) 来支持拦截器。恕我直言,这不是最佳实践,但是这可能是有争议的。起初,如果它没有创建自己的请求类型(InterceptingClientHttpRequest),那么这里没有任何问题。根据定义,该请求类型是 AbstractBufferingClientHttpRequest。好吧,我们尝试在这里直播...

好吧,不用担心,它的行为有点像实际请求的代理......好吧,不是真的。仅当请求被“执行”时,它才会实例化实际的底层(流)请求类型。讽刺的是,InterceptingClientHttpRequest 用于在通过消息转换器编写请求正文时准备请求正文(这发生在执行请求之前)。在其超类 AbstractHttpMessageConverter 中,它检查请求是否是 StreamingHttpOutputMessage 的实例,在这种情况下,它准备流式传输。

但是该代码永远不会被执行,因为在此级别上,请求始终是 InterceptingClientHttpRequest,而不是 StreamingHttpOutputMessage。

我尝试以下方法来绕过此行为:我确保在 HttpComponentsStreamingClientHttpRequest 上设置主体,但这根本不起作用,因为请求的输出流底层是 ContentLengthOutputStream,它实际上只是忽略所有写入的字节,因为它检查是否字节数组的长度小于内容长度(设置为流媒体的默认值,流派 0 或 -1)。无论如何,HttpComponentsStreamingClientHttpRequest 中的 StreamingHttpEntity 类对于属性 chunked 返回 false (这是我期望的值,逐块刷新)。

据我所知,通过 SimpleClientHttpRequestFactory 的 SimpleStreamingClientHttpRequest 不太可能工作,因为它与资源没有链接。如果它在与消息转换器交互时已经创建,它可能会起作用,但事实并非如此,请参阅前面的解释。另外,SimpleStreamingClientHttpRequest 不是 StreamingHttpOutputMessage,而 HttpComponentsStreamingClientHttpRequest 实际上是。

最后一点:我发现它根本不是流式传输的,因为我必须发送一个超过 2.2 GB 的文件,这基本上是 Java 中字节数组的限制,因为它的长度只能是 MAX_INT long 。所使用的 ByteArrayOutputStream 将其作为最大大小(显然),并检查归结为 ArraysSupport.hugeLength。当您超过该长度时,这会错误地抛出 OutOfMemoryError 。那应该是一个 RuntimeException。一开始我很困惑,因为我实际上认为 JVM 有堆大小问题(事实完全不是这样)。

所以任何人都知道我在哪里做错了。错误的假设?预先感谢。

java spring streaming resttemplate
1个回答
0
投票

目前正在使用流发布文件,并且运行良好。 也许出于测试目的,尝试获取一个您知道正在流中工作的 http 请求,然后将 http 标头显式添加到请求中,以及 buffered=false。 但你应该再次知道这是可能的。

© www.soinside.com 2019 - 2024. All rights reserved.