12

我有一个关于 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 项目链接

样本输出:

all ids are 176
finished !!! 171

请给我建议如何调试或我做错了什么。帮助表示赞赏。

更新:

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

onNext(ReactorClientHttpResponse{request=[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,而在于其他地方。环境或服务器阻止了请求,但可能我应该收到一些错误日志。

附言。谢谢您的帮助 !

4

1 回答 1

6

这是一个棘手的问题。调试收到的实际 HTTP 帧,似乎我们真的没有得到某些请求的响应。使用 Wireshark 进行更多调试,看起来远程服务器正在使用FIN, ACKTCP 数据包请求结束连接,并且客户端确认了它。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();

潜在问题

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

于 2017-07-07T07:51:44.153 回答