我对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);
问题是如果我提出更多请求&gt; 100。 我没有获得所有响应,没有抛出错误或返回错误响应代码,并且调用的subscribe方法的大小与请求数不同。
我提出的请求基于字符串列表(网址),在发出所有响应之后,我应该将所有响应作为列表接收,因为我正在使用collectList()
。当我执行100个请求时,我希望收到100个响应的列表,但实际上我有时会收到100个,有时候会收到96个等等...可能是某些东西无声地失败了。
这很容易重现,这是我的github项目link。
示例输出:
all ids are 176
finished !!! 171
请给我建议我如何调试或我做错了什么。感谢帮助。
更新
日志显示我是否传递了126个网址,例如:
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中,而在于其他地方。环境或服务器阻止请求,但可能是我应该收到一些错误日志。
PS。谢谢你的帮助!
答案 0 :(得分:2)
这是一个棘手的问题。调试收到的实际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客户端在关闭时不应重用连接。当我知道更多时,我会在这里报告。