反应式 WebClient 未发出响应

Posted

技术标签:

【中文标题】反应式 WebClient 未发出响应【英文标题】:Reactive WebClient not emitting a response 【发布时间】:2017-12-09 08:54:53 【问题描述】:

我有一个关于 Spring Reactive WebClient 的问题... 几天前,我决定使用 Spring Framework 中的新反应式东西,并制作了一个小项目,仅出于个人目的抓取数据。 (向一个网页发出多个请求并组合结果)。

我开始使用新的响应式 WebClient 来发出请求,但我发现的问题是客户端没有为每个请求发出响应。听起来很奇怪。这是我为获取数据所做的:

private Mono<String> fetchData(String uri) 
    return this.client
            .get()
            .uri(uri)
            .header("X-Fsign","SW9D1eZo")
            .retrieve()
            .bodyToMono(String.class)
            .timeout(Duration.ofSeconds(35))
            .log("category", Level.ALL, SignalType.ON_ERROR, SignalType.ON_COMPLETE, SignalType.CANCEL, SignalType.REQUEST);

以及调用fetchData的函数:

public Mono<List<Stat>> fetch() 
    return fetchData(URL)
            .map(this::extractUrls)
            .doOnNext(System.out::println)
            .doOnNext(s-> System.out.println("all ids are "+s.size()))
            .flatMapIterable(q->q)
            .map(s -> s.substring(7, 15))
            .map(s -> "http://d.flashscore.com/x/feed/d_hh_" + s + "_en_1") // list of N-length urls
            .flatMap(this::fetchData)
            .map(this::extractHeadToHead)
            .collectList();

和订阅者:

    FlashScoreService bean = ctx.getBean(FlashScoreService.class);
    bean.fetch().subscribe(s->
        System.out.println("finished !!! " + s.size()); //expecting same N-length list size
    ,Throwable::printStackTrace);

问题是如果我提出的请求多一点 > 100。 我没有得到所有的响应,没有抛出错误或返回错误响应代码,并且调用了 subscribe 方法,其大小与请求数不同。

我提出的请求基于字符串列表 (url),在发出所有响应后,我应该将所有响应作为列表接收,因为我使用的是 collectList()。当我执行 100 个请求时,我希望收到 100 个响应的列表,但实际上我有时会收到 100 个,有时会收到 96 个等……可能是某些事情默默地失败了。 这很容易重现,这是我的 github 项目link。

样本输出:

all ids are 176
finished !!! 171

请给我建议如何调试或我做错了什么。感谢您的帮助。

更新:

如果我传递了 126 个 url,例如日志显示:

onNext(ReactorClientHttpResponserequest=[GET/some_url],status=200) is called 121 times. May be here is the problem.
onComplete() is called 126 times which is the exact same length of the passed list of urls

但是如何在不调用 onNext() 或 onError( ) 的情况下完成一些请求? (Mono 中的成功与错误)

我认为问题不在于 WebClient,而在于其他地方。环境或服务器阻止了请求,但可能我应该收到一些错误日志。

ps。感谢您的帮助!

【问题讨论】:

你添加的日志操作符应该会显示很多信息;你能分享一下这几个案例会发生什么吗? 请看我的更新。谢谢 【参考方案1】:

这是一个棘手的问题。调试收到的实际 HTTP 帧,似乎我们真的没有得到某些请求的响应。使用 Wireshark 进行更多调试,看起来远程服务器正在使用FIN, ACK TCP 数据包请求结束连接,并且客户端确认了它。问题是在第一个FIN, ACK TCP 数据包之后,此连接仍然从池中获取以发送另一个 GET 请求。

也许远程服务器在处理了许多请求后正在关闭连接;无论如何,这是完全合法的行为。请注意,我不会始终如一地复制此内容。

解决方法

您可以在客户端禁用连接池;这会更慢,显然不会触发这个问题。为此,请使用以下内容:

this.client = WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(new Consumer<HttpClientOptions.Builder>() 
                    @Override
                    public void accept(HttpClientOptions.Builder builder) 
                        builder.disablePool();
                    
                ))
                .build();

根本问题

根本问题是当 TCP 连接关闭而不发送响应时,HTTP 客户端不应该onComplete。或者更好的是,HTTP 客户端在关闭连接时不应重用它。当我知道更多时,我会在这里报告。

【讨论】:

您好,首先感谢您的支持。对此,我真的非常感激 。使用 Wireshark 进行的所有调试和数据包检查都非常感谢!我是整个 Spring 生态系统的忠实粉丝。惊人的工作!关于我的问题..是的,每次都无法重现,这对我来说是最大的担忧。感谢您的解释和解决方法,它将对我有所帮助。我应该将问题标记为已回答,并将继续关注您 jira 上的小错误。再次感谢 归档SPR-15784。 @NikolayRusev 我创建了github.com/reactor/reactor-netty/issues/138 - 这应该是这里的实际潜在问题 使用reactor-netty-0.6.4,没有HttpClientOptions.Builder;方法签名是public ReactorClientHttpConnector(Consumer&lt;? super HttpClientOptions&gt; clientOptions)。但我使用Consumer&lt;HttpClientOptions&gt; 完成了您所展示的操作,并且似乎可以正常工作。

以上是关于反应式 WebClient 未发出响应的主要内容,如果未能解决你的问题,请参考以下文章

Spring Reactive Webclient 的请求级背压?

如何使用 WebClient 使用响应式 Spring Rest API

如何使用 WebClient 反应式 Web 客户端发送带有 zip 正文的 POST 请求

如何在 Spring 响应式 WebClient 中返回 Kotlin Coroutines Flow

Spring webclient exchangeToFlux()不发出HTTP请求

如何使用 Spring Boot WebClient 收集分页 API 响应?