使用WebClient进行非阻塞REST调用时的重大故障率

时间:2020-10-12 18:37:46

标签: java spring rest nonblocking spring-webclient

我正在编写一个扫描在线商店之一的应用程序,并且正在那里寻找新的报价。我使用的官方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>

0 个答案:

没有答案