我正在编写一个扫描在线商店之一的应用程序,并且正在那里寻找新的报价。我使用的官方REST API每秒最多允许9000个请求(每个clientId),并且要求我检查大约350个不同的查询,所以现在我每3秒调用一次此API。我最初的方法是使用阻塞RestTemplate
并产生多达350个线程,每个线程用于一个查询。它奏效了,但是我想尝试使用WebClient
的非阻塞方法。在这里问题开始加剧。
我的代码非常简单,这是我的非阻塞客户端
@Component
public class OffersListingReactiveClient {
private final AuthenticationService authenticationService;
private final WebClient webClient;
public OffersListingReactiveClient(final AuthenticationService authenticationService, final WebClient webClient) {
this.authenticationService = authenticationService;
this.webClient = webClient;
}
public Mono<ApiResponse> collectData(final SearchQuery query) {
final var accessToken = authenticationService.getAccessToken();
return invoke(query.toUrlParams(), accessToken);
}
private Mono<ApiResponse> invoke(final String endpoint, final String accessToken) {
return webClient.get()
.uri(endpoint)
.header("Authorization", accessToken)
.retrieve()
.bodyToMono(ApiResponse.class)
.onErrorReturn(ApiResponse.emptyResponse());
}
}
与使用该客户端的部分服务无关,我想其余的代码都是无关紧要的,我要在几个不同的线程中用initialItems
初始化RestClient
,一旦数据初始化,我发布事件会携带这些数据,从那一刻起,我便开始使用下面的代码收集新项目。
@Scheduled(fixedRate = 3000)
private void collectData() {
if (!initialItems.isEmpty()) {
try {
queryRepository.getSearchQueries()
.forEach(query -> reactiveClient.collectData(query)
.subscribe(response -> {
this.processResponse(response);
this.logResults(response);
}));
} catch (Throwable t) {
logger.severe("Throwable " + t.getMessage());
}
}
}
private void processResponse(final ApiResponse response) {
response.collectItems().forEach(item -> {
if (!initialItems.containsKey(item.getId())) {
initialItems.put(item.getId(), item);
eventPublisher.publishEvent(new NewItemEvent(this, item));
}
});
}
private void logResults(final ApiResponse response) {
if (response.isFailed()) {
failRate.incrementAndGet();
} else {
successRate.incrementAndGet();
}
logger.info("Success rate: " + successRate + ", Failure rate: " + failRate);
}
如您所见,我添加了logResults()
方法来简单地查看有多少个请求失败并且该请求数量巨大。
350个请求/ 3秒内,超过70%的请求失败!
175个请求/ 3秒,其成功/失败率约为50%。
在80个请求/ 3秒的时间内,失败率约为10%。
40次请求/ 3秒,大约是1%。
那是为什么?在40个/ 80个请求中,我遇到了这个异常
2020-10-12 20:19:08.345警告5676 --- [ctor-http-nio-7] r.netty.http.client.HttpClientConnect:[id:0xedf175d7, L:/192.168.1.11:62743! R:api.pl/xxxxx:443]该连接观察到 错误
reactor.netty.http.client.PrematureCloseException:连接 响应之前过早关闭
一旦我添加了更多查询(意味着更多请求),我也将得到这个查询
io.netty.handler.ssl.SslHandshakeTimeoutException:握手超时 在10000ms后 io.netty.handler.ssl.SslHandler $ 5.run(SslHandler.java:2062) 〜[netty-handler-4.1.52.Final.jar:4.1.52.Final]禁止显示: Reactor.core.publisher.FluxOnAssembly $ OnAssemblyException:发生错误 在以下站点被观察到:| _检查点⇢请求 得到 https://api.pl/offers/listing?phrase=ryzen&include=-all&include=items&sort=-startTime&category.id=42540aec-367a-4e5e-b411-17c09b08e41f [DefaultWebClient]堆栈跟踪:位于 io.netty.handler.ssl.SslHandler $ 5.run(SslHandler.java:2062) 〜[netty-handler-4.1.52.Final.jar:4.1.52.Final]在 io.netty.util.concurrent.PromiseTask.runTask(PromiseTask.java:98) 〜[netty-common-4.1.52.Final.jar:4.1.52.Final]在 io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:170) 〜[netty-common-4.1.52.Final.jar:4.1.52.Final]
编辑#
我要添加依赖项
<properties>
<java.version>14</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>