在请求写入完成之前,WebClient 不会读取响应. [英] WebClient doesn't read response until request write is completed.

查看:21
本文介绍了在请求写入完成之前,WebClient 不会读取响应.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现流媒体代理.我遇到了 Spring 响应式 WebClient 的问题.

I'm trying to implement streaming proxy. I faced with the issue with WebClient from spring reactive.

谁能帮我理解我是不是有什么错误的方式,或者这只是 WebClient 端的一个错误?

Could anyone help me to understand Do I some wrong way or it's just a bug from WebClient side?

堆栈:

reactor-netty 0.7.8.RELEASE

spring-boot 2.0.4.RELEASE

描述:

Desc:

我想将一个长流代理到外部服务,然后将响应流转发给请求者.流式传输使用块(HTTP 1.1 Transfer-Encoding : chunked).外部服务处理每个块并发送到响应结果.

I want to proxy a long stream to external service and then forward stream of responses to the requester. Streaming is happening using chunks (HTTP 1.1 Transfer-Encoding : chunked). External service processes every chunk and sends to the response result.

预期行为:

WebClient 应立即读取收到的每个响应部分.

WebClient should read every received part of response immediately.

实际行为:

WebClient 在请求写入完成之前不会启动进程响应.

WebClient doesn't start process response until request write is completed.

代码:

return client
    .post()
    .header("Transfer-Encoding", "chunked")
//because I want to flush each received part
    .body((outputMessage, context) -> outputMessage.writeAndFlushWith(
        request.body(BodyExtractors.toDataBuffers())
               .map(dataBuffer -> Mono.just(dataBuffer))))
    .exchange()
    .flatMap(clientResponse -> {
      ServerResponse.BodyBuilder bodyBuilder = ServerResponse.status(clientResponse.statusCode());
      bodyBuilder.contentType(MediaType.APPLICATION_STREAM_JSON);

      return bodyBuilder.body((outputMessage, context) ->                                                        
          outputMessage.writeAndFlushWith(                                               
            clientResponse.body(BodyExtractors.toDataBuffers())                                                               
                          .map(dataBuffer -> Mono.just(dataBuffer))
                         ));}
);

推荐答案

我查了一下,似乎按照设计,Spring WebFlux 的 WebClient 和 Reactor Netty HttpClient被设计成先处理请求处理(发送请求体),然后读取响应体.

I've looked it up and it seems that by design, both Spring WebFlux's WebClient and Reactor Netty HttpClient are designed to first handle the request processing (sending the request body), and then reading the response body.

其他 HTTP 客户端可能允许这样做,但我认为在这种情况下,这是一种在读/写操作上链接背压并将所有内容链接为单个反应式管道的方法.

Other HTTP clients might allow this, but I think that in this case this is a way to link backpressure on both read/write operations and chain everything as a single reactive pipeline.

您可能正在寻找具有背压支持的面向消息的双向传输协议.你可以看看 WebSockets(虽然你需要在那里定义你自己的消息语义)或者关注 RSocket.

You might be looking for a message-oriented, bi-directionnal transport protocol with backpressure support. You can take a look at WebSockets (although you'll need to define your own message semantics there) or keep an eye on RSocket.

如果你只是在寻找一个高效的反应式网关,那么 Spring Cloud Gateway 是您最好的选择,因为它始终具有响应性并支持有趣的附加功能.

If you're just looking for an efficient reactive gateway, then Spring Cloud Gateway is your best bet since it's reactive all the way down and supports interesting additional features.

一些附加说明:

Spring WebFlux(在客户端和服务器级别)使用 EncoderDecoder 实现,适应消息 Content-Type.一些特定的内容类型,例如 application/streaming+jsontext/event-stream 是在考虑流场景的情况下实现的.这意味着编码器正在写入消息,以特定字符分隔,并在网络上刷新.使用诸如 application/octet-streamapplication/json 之类的常规媒体类型不会触发该行为.对于这些情况,代理和中介可能会缓冲消息正文并提供更大/更小的窗口.这就是为什么此类机制需要在消息和适当的编解码器之间使用分隔符.

Spring WebFlux (at both client and server levels) uses Encoder and Decoder implementations, adapting to the message Content-Type. Some specific content-types, such as application/streaming+json or text/event-stream are implemented with streaming scenarios in mind. This means that encoders are writing messages as they come, separated with specific characters, and flushing on the network. Using regular media types such as application/octet-stream or application/json will not trigger that behavior. For those cases, proxies and intermediaries might buffer the message body and deliver bigger/smaller windows. This is why such mechanisms require a separator between messages and proper codecs.

据我所知,您使用的是 HTTP 1.1,它使用请求/响应机制 - HTTP 规范没有明确禁止服务器在读取完整请求之前写入响应,但它确实说它无论如何都必须阅读完整的请求正文(或关闭连接).请参阅 https://tools.ietf.org/html/rfc7230#section-3.4

As far as I understand, you're using HTTP 1.1 which is using a request/response mechanism - the HTTP spec doesn’t explicitly forbid te server from writing the response before reading the full request, but it does say that it has to read the full request body (or close the connection) no matter what. See https://tools.ietf.org/html/rfc7230#section-3.4

与往常一样,您可以在 https://jira.spring.io 上请求增强功能,尽管在此案例,我认为这是设计使然.

As always, you can request enhancements on https://jira.spring.io, although in this case, I think this is done by design.

这篇关于在请求写入完成之前,WebClient 不会读取响应.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆