4

我正在尝试实现流代理。我从 spring 反应中遇到了 WebClient 的问题。

任何人都可以帮助我理解我是不是有些错误的方式,或者这只是 WebClient 方面的错误?

堆:

反应堆-netty 0.7.8.RELEASE

spring-boot 2.0.4.RELEASE

描述:

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

预期行为:

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

实际行为:

WebClient 在请求写入完成之前不会开始处理响应。

代码:

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))
                         ));}
);
4

2 回答 2

2

刚刚测试了基于 Jetty 的 WebClient 实现,它的行为与您预期的一样。它可以在发送所有请求内容之前开始读取响应。它应该已经在 Spring Framework 5.1 WebClient on Jetty 新功能问题上发布

于 2018-10-22T09:40:29.977 回答
1

我查了一下,似乎在设计上,Spring WebFluxWebClient和 Reactor NettyHttpClient都设计为首先处理请求处理(发送请求正文),然后读取响应正文。

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

您可能正在寻找具有背压支持的面向消息的双向传输协议。您可以查看 WebSockets(尽管您需要在那里定义自己的消息语义)或密切关注RSocket

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

一些附加说明:

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

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

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

于 2018-08-31T21:09:40.327 回答