Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/379.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何提出真正的流量请求?_Java_Spring Boot_Spring Webflux - Fatal编程技术网

Java 如何提出真正的流量请求?

Java 如何提出真正的流量请求?,java,spring-boot,spring-webflux,Java,Spring Boot,Spring Webflux,我开始从Spring boot学习Webflux。我了解到,对于RestController的端点,您可以定义一个流量请求主体,在这里我期望一个真正的流量流,也就是说,整个请求的各个部分是一个接一个地出现的,这些部分也可以一个接一个地处理。然而,在用一个客户端和一个服务器构建了一个小示例之后,我无法按预期工作 下面是服务器的代码片段: @PostMapping(“/digest”) 公共流量摘要(@RequestBody流量文本){ continuousMD5.reset(); 返回text.l

我开始从Spring boot学习Webflux。我了解到,对于RestController的端点,您可以定义一个流量请求主体,在这里我期望一个真正的流量流,也就是说,整个请求的各个部分是一个接一个地出现的,这些部分也可以一个接一个地处理。然而,在用一个客户端和一个服务器构建了一个小示例之后,我无法按预期工作

下面是服务器的代码片段:

@PostMapping(“/digest”)
公共流量摘要(@RequestBody流量文本){
continuousMD5.reset();
返回text.log(“server.request.”).map(片断->continuousMD5.update(片断)).log(“server.response.”);
}
注意:文本的每一部分都将被发送到一个continuousMD5对象,该对象将累加所有部分,并在每次累加后计算并返回中间MD5哈希值。将在MD5计算之前和之后记录流

下面是客户端的代码片段:

@PostConstruct
    private void init() {
        webClient = webClientBuilder.baseUrl(reactiveServerUrl).build();
    }

@PostMapping(value = "/send", consumes = MediaType.TEXT_PLAIN_VALUE)
    public Flux<String> send(@RequestBody Flux<String> text) {
        return webClient.post()
            .uri("/digest")
            .accept(MediaType.TEXT_PLAIN)
            .body(text.log("client.request."), String.class)
            .retrieve().bodyToFlux(String.class).log("client.response.");
    }
2019-05-09 17:02:08.604  INFO 3462 --- [ctor-http-nio-2] client.response.Flux.MonoFlatMapMany.2   : onSubscribe(MonoFlatMapMany.FlatMapManyMain)
2019-05-09 17:02:08.606  INFO 3462 --- [ctor-http-nio-2] client.response.Flux.MonoFlatMapMany.2   : request(1)
2019-05-09 17:02:08.649  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : onSubscribe(FluxSwitchIfEmpty.SwitchIfEmptySubscriber)
2019-05-09 17:02:08.650  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(32)
2019-05-09 17:02:08.674  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : onNext(The message 1)
2019-05-09 17:02:08.676  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.676  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : onNext(The message 2)
...
2019-05-09 17:02:08.710  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : onNext(The message 100)
2019-05-09 17:02:08.710  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.710  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.710  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.711  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : onComplete()
2019-05-09 17:02:08.711  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.711  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.860  INFO 3462 --- [ctor-http-nio-6] client.response.Flux.MonoFlatMapMany.2   : onNext(CSubeSX3yIVP2CD6FRlojg==)
2019-05-09 17:02:08.862  INFO 3462 --- [ctor-http-nio-6] client.response.Flux.MonoFlatMapMany.2   : onComplete()
^C2019-05-09 17:02:47.393  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : cancel()
我可以在客户的日志中看到:

@PostConstruct
    private void init() {
        webClient = webClientBuilder.baseUrl(reactiveServerUrl).build();
    }

@PostMapping(value = "/send", consumes = MediaType.TEXT_PLAIN_VALUE)
    public Flux<String> send(@RequestBody Flux<String> text) {
        return webClient.post()
            .uri("/digest")
            .accept(MediaType.TEXT_PLAIN)
            .body(text.log("client.request."), String.class)
            .retrieve().bodyToFlux(String.class).log("client.response.");
    }
2019-05-09 17:02:08.604  INFO 3462 --- [ctor-http-nio-2] client.response.Flux.MonoFlatMapMany.2   : onSubscribe(MonoFlatMapMany.FlatMapManyMain)
2019-05-09 17:02:08.606  INFO 3462 --- [ctor-http-nio-2] client.response.Flux.MonoFlatMapMany.2   : request(1)
2019-05-09 17:02:08.649  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : onSubscribe(FluxSwitchIfEmpty.SwitchIfEmptySubscriber)
2019-05-09 17:02:08.650  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(32)
2019-05-09 17:02:08.674  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : onNext(The message 1)
2019-05-09 17:02:08.676  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.676  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : onNext(The message 2)
...
2019-05-09 17:02:08.710  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : onNext(The message 100)
2019-05-09 17:02:08.710  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.710  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.710  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.711  INFO 3462 --- [ctor-http-nio-2] client.request.Flux.SwitchIfEmpty.1      : onComplete()
2019-05-09 17:02:08.711  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.711  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.860  INFO 3462 --- [ctor-http-nio-6] client.response.Flux.MonoFlatMapMany.2   : onNext(CSubeSX3yIVP2CD6FRlojg==)
2019-05-09 17:02:08.862  INFO 3462 --- [ctor-http-nio-6] client.response.Flux.MonoFlatMapMany.2   : onComplete()
^C2019-05-09 17:02:47.393  INFO 3462 --- [ctor-http-nio-6] client.request.Flux.SwitchIfEmpty.1      : cancel()
每个文本片段都被认为是流量流的一个元素,并被单独请求

但在服务器日志中:

2019-05-09 17:02:08.811  INFO 3475 --- [ctor-http-nio-2] server.request.Flux.SwitchIfEmpty.1      : onSubscribe(FluxSwitchIfEmpty.SwitchIfEmptySubscriber)
2019-05-09 17:02:08.813  INFO 3475 --- [ctor-http-nio-2] server.response.Flux.Map.2               : onSubscribe(FluxMap.MapSubscriber)
2019-05-09 17:02:08.814  INFO 3475 --- [ctor-http-nio-2] server.response.Flux.Map.2               : request(1)
2019-05-09 17:02:08.814  INFO 3475 --- [ctor-http-nio-2] server.request.Flux.SwitchIfEmpty.1      : request(1)
2019-05-09 17:02:08.838  INFO 3475 --- [ctor-http-nio-2] server.request.Flux.SwitchIfEmpty.1      : onNext(The message 1The message 2The message 3The message 4The message 5The message 6The message 7The message 8The message 9The message 10The message 11The message 12The message 13The message 14The message 15The message 16The message 17The message 18The message 19The message 20The message 21The message 22The message 23The message 24The message 25The message 26The message 27The message 28The message 29The message 30The message 31The message 32The message 33The message 34The message 35The message 36The message 37The message 38The message 39The message 40The message 41The message 42The message 43The message 44The message 45The message 46The message 47The message 48The message 49The message 50The message 51The message 52The message 53The message 54The message 55The message 56The message 57The message 58The message 59The message 60The message 61The message 62The message 63The message 64The message 65The message 66The message 67The message 68The message 69The message 70The message 71The message 72The message 73The message 74The message 75The message 76The message 77The message 78The message 79The message 80The message 81The message 82The message 83The message 84The message 85The message 86The message 87The message 88The message 89The message 90The message 91The message 92The message 93The message 94The message 95The message 96The message 97The message 98The message 99The message 100)
2019-05-09 17:02:08.840  INFO 3475 --- [ctor-http-nio-2] server.response.Flux.Map.2               : onNext(CSubeSX3yIVP2CD6FRlojg==)
2019-05-09 17:02:08.852  INFO 3475 --- [ctor-http-nio-2] server.response.Flux.Map.2               : request(32)
2019-05-09 17:02:08.852  INFO 3475 --- [ctor-http-nio-2] server.request.Flux.SwitchIfEmpty.1      : request(32)
2019-05-09 17:02:08.852  INFO 3475 --- [ctor-http-nio-2] server.request.Flux.SwitchIfEmpty.1      : onComplete()
2019-05-09 17:02:08.852  INFO 3475 --- [ctor-http-nio-2] server.response.Flux.Map.2               : onComplete()
2019-05-09 17:02:47.394  INFO 3475 --- [ctor-http-nio-2] server.response.Flux.Map.2               : cancel()
2019-05-09 17:02:47.394  INFO 3475 --- [ctor-http-nio-2] server.request.Flux.SwitchIfEmpty.1      : cancel()
我看到所有的文本片段一次到达服务器,因此被作为通量流中的一个大元素处理(还可以验证是否只计算了一个MD5哈希,而不是100)

我所期望的是,服务器还将从客户端接收文本片段作为流量流中的元素,否则对于服务器来说,它不是真正的反应性请求,而只是一个正常的阻塞请求

有人能帮我理解如何使用Webflux发出真正的flux响应请求吗?谢谢

更新


我使用一个类似的命令行对服务器发出REST请求,可以看到服务器接收到的文本片段(“消息x”)是一个流量流。因此,我想服务器是正常的,现在问题可能是客户端:我如何使用WebClient发出真正的flux REST请求?

如果您想实现流媒体效果,您可以:

  • 使用支持流式传输的不同内容类型-
    应用程序/stream+json
    。查看以下关于它的SO线程:
  • 将底层协议更改为更适合流媒体模型的协议,例如WebSocket
在尝试和阅读了更多的文档之后,我终于找到了如何让我的示例发挥作用的方法:

对于客户端,我需要确保发送到服务器的请求正文也由换行符分隔:

@PostMapping(value=“/send”,consumes=MediaType.TEXT\u PLAIN\u value)
公共流量发送(@RequestBody流量文本){
返回webClient.post()
.uri(“/摘要”)
.accept(MediaType.TEXT\u PLAIN)
.身体(
文本
.onBackpressureBuffer()
.log(“client.request”)
.map(工件->工件+“\n”),
字符串(类)
.retrieve().bodyToFlux(String.class)
.onBackpressureBuffer()
.log(“client.response”);
}
这实现了与通过命令行发出REST请求相同的效果,如
for i in$(seq 1 100);做回显“消息$i”;完成
按行输出“消息x”

类似地,对于服务器,响应主体也需要通过换行分隔,以便客户端可以将主体解码为流量:

@PostMapping(“/digest”)
公共流量摘要(@RequestBody流量文本){
continuousMD5.reset();
返回文本
.log(“server.request”)
.map(件->连续MD5.update(件))
.map(工件->工件+“\n”)
.log(“server.response.”);
}
在发送之前和接收之后,我还向客户端添加了
onBackpressureBuffer()
,以便在发送大量消息时不会出现溢出异常


然而,尽管上面的代码“有效”,但它并不是真正的流式处理,正如我在日志中看到的,服务器在客户端发送整个请求体之后开始接收请求体,而客户端在服务器发送整个响应体之后开始接收响应体。也许正如Ilya Zinkovich所提到的,使用WebSocket协议可以实现真正的流式传输效果,但我还没有尝试过。

反应式模型仍然映射到HTTP,我很难理解“片段”的含义:HTTP在离散请求中运行,它定义了事件的边界。因此,当我发送REST请求时,“message x”从1到100,客户机一个接一个地接收到这些消息,或者如果我猜,Webflux框架将整个主体拆分为flux流的元素。我希望服务器做同样的事情,因为客户端也发送一个流量流。但是我看到服务器将通量流的所有元素作为一个大块接收,并且没有“拆分”通量流中的元素。我希望这能更好地解释。谢谢你指出这些要点!我甚至没有想到mime类型会产生影响。但是,如果我阅读webflux的文档,在使用flux作为请求主体时,对mime类型或WebSocket协议没有明确的要求。所以我认为webflux足够智能,可以检测到传入的请求,并尝试将主体解码为flux。是的,文档中没有这么清楚。但最令人困惑的是HTTP和反应式模型之间的差异——想象一下,如果在响应中使用application/json内容类型返回无限流量,会发生什么。一般来说,总是用自动化测试来验证您的期望是很重要的,因为默认行为甚至可以在库的主要版本之间更改。