如何从 WebFlux 客户端正确排出/释放响应体?

Posted

技术标签:

【中文标题】如何从 WebFlux 客户端正确排出/释放响应体?【英文标题】:How to properly drain / release response body from WebFlux Client? 【发布时间】:2019-09-04 09:02:42 【问题描述】:

我正在使用 Spring 5 响应式堆栈中的 WebFlux HTTP 客户端来访问外部 REST 服务。我想根据 HTTP 状态处理响应:

    如果状态为 2xx,我想返回带有反序列化响应正文的 Mono

    如果状态为 404,我想删除响应正文并立即返回空 Mono

    对于任何其他状态,我想删除响应正文并返回带有 MyBusinessException 的错误 Mono。

我的代码如下所示:

webClient.get()
    .uri("/search")
    .syncBody(request)
    .exchange()
    .flatMap  response ->
        when 
            response.statusCode().is2xxSuccessful -> response.bodyToMono(MyResponse::class.java)
            response.statusCode() == NOT_FOUND -> Mono.empty()
            else -> MyBusinessException().toMono<MyResponse>()
        
     

我不想在不需要时浪费时间接收和处理响应正文。 exchange() 方法状态的 JavaDoc

您必须始终使用响应的主体或实体方法之一来确保资源被释放。

如果我想排空响应正文并立即返回结果,我应该怎么做?

【问题讨论】:

【参考方案1】:

需要排空响应,以便可以将连接重用于未来的请求(即 http 保持活动/持久连接)。

返回一个空的Mono,在主体被耗尽后完成(忽略错误):

// Using WebFlux >= 5.2
response.releaseBody()
    // ignore errors
    .onErrorResume(exception -> Mono.empty());


// Using WebFlux < 5.2
response.body(BodyExtractors.toDataBuffers())
    // release DataBuffers
    .doOnNext(DataBufferUtils::release)
    // ignore errors
    .onErrorResume(exception -> Mono.empty())
    // return an empty Mono
    .then();

返回一个立即完成的空Mono,并在后台异步耗尽正文(忽略错误):

// Using WebFlux >= 5.2
Mono.<Void>empty()
    .doOnSubscribe(s ->
        // drain the body
        response.releaseBody()
            // initiate drain on a separate Scheduler
            .subscribeOn(Schedulers.parallel())
            // subscribe, and ignore errors
            .subscribe())

// Using WebFlux < 5.2
Mono.<Void>empty()
    .doOnSubscribe(s ->
        // drain the body
        response.body(BodyExtractors.toDataBuffers())
            // release DataBuffers
            .doOnNext(DataBufferUtils::release)
            // initiate drain on a separate Scheduler
            .subscribeOn(Schedulers.parallel())
            // subscribe, and ignore errors
            .subscribe())

我仍然推荐第一个选项,因为它会立即释放资源,并且可能是 WebClient 开发人员在编写和记录其使用情况时所考虑的。

我从未在生产系统中使用过第二个选项,因此请自行测试以确保 http 连接池的行为符合预期。如果使用 reactor-netty,您可以在 reactor.netty.resources.PooledConnectionProvider 上启用调试日志记录以比较两种方法。

【讨论】:

谢谢。这个Mono 将在收到整个响应后完成。如何在后台消耗响应时返回空的Mono?这更多的是 Reactor 问题,但我对这个主题很陌生,害怕犯错误,比如阻塞事件循环、没有正确订阅等 答案已编辑以包含在后台排水的示例。

以上是关于如何从 WebFlux 客户端正确排出/释放响应体?的主要内容,如果未能解决你的问题,请参考以下文章

Spring WebFlux,提取请求体

Webflux WebClient中如何自定义异常?

Spring WebFlux 没有流式响应

禁用传输编码:在 Spring Webflux 响应中分块

服务器发送事件 Spring Webflux 返回事件名称以响应来自休息控制器的响应

怎么正确响应WM_NCLBUTTONDOWN消息