我正在尝试实现流代理。 我从spring react遇到了WebClient的问题。
任何人都可以帮助我了解我是否使用了错误的方式,或者仅仅是WebClient方面的错误?
堆栈:
反应堆净值0.7.8。发布
spring-boot 2.0.4.RELEASE
描述:
我想将一长串流代理到外部服务,然后将响应流转发到请求者。 流使用块来进行(HTTP 1.1 Transfer-Encoding:块化)。 外部服务处理每个块并发送到响应结果。
预期行为:
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))
));}
);
答案 0 :(得分:0)
我已经查看了它,看来Spring WebFlux的WebClient
和Reactor Netty HttpClient
都设计为首先处理请求处理(发送请求主体),然后读取响应主体。
其他HTTP客户端可能允许这样做,但是我认为在这种情况下,这是一种在读/写操作上链接反压并将所有内容链接为单个反应性管道的方法。
您可能正在寻找带有背压支持的面向消息的双向传输协议。您可以看一下WebSockets(尽管您需要在那里定义自己的消息语义)或关注RSocket。
如果您只是在寻找高效的反应式网关,那么Spring Cloud Gateway是您最好的选择,因为它一直是反应式的,并支持有趣的附加功能。
一些附加说明:
Spring WebFlux(在客户端和服务器级别)都使用Encoder
和Decoder
实现,以适应消息Content-Type。某些特定的内容类型(例如application/streaming+json
或text/event-stream
)是在考虑流场景的情况下实现的。这意味着编码器在写入消息时用特定字符分隔并在网络上刷新它们。使用常规媒体类型,例如application/octet-stream
或application/json
不会触发该行为。对于这些情况,代理和中介可能会缓冲邮件正文并传递更大或更小的窗口。这就是为什么这种机制要求在消息和适当的编解码器之间使用分隔符。
据我了解,您使用的是HTTP 1.1,该协议使用了请求/响应机制-HTTP规范并未明确禁止服务器在读取完整请求之前写入响应,但它确实说无论如何,都必须读取完整的请求正文(或关闭连接)。参见https://tools.ietf.org/html/rfc7230#section-3.4
和往常一样,您可以请求对https://jira.spring.io进行增强,尽管在这种情况下,我认为这是设计使然。
答案 1 :(得分:0)
仅测试了基于Jetty的WebClient实施,它的行为符合您的预期。它可以在发送所有请求内容之前开始读取响应。 它应该已经在Spring Framework 5.1中发布了 WebClient on Jetty new feature issue