反应式 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<? super HttpClientOptions> clientOptions)
。但我使用Consumer<HttpClientOptions>
完成了您所展示的操作,并且似乎可以正常工作。以上是关于反应式 WebClient 未发出响应的主要内容,如果未能解决你的问题,请参考以下文章
Spring Reactive Webclient 的请求级背压?
如何使用 WebClient 使用响应式 Spring Rest API
如何使用 WebClient 反应式 Web 客户端发送带有 zip 正文的 POST 请求
如何在 Spring 响应式 WebClient 中返回 Kotlin Coroutines Flow