HttpClient asyncRequest 和指数退避
HttpClient asyncRequest and exponential backoff
我需要对可能失败的请求实施指数退避。但是,它是作为异步请求实现的。如果这是同步完成的,我会更好地了解将延迟放在哪里。粗略地说,我认为它会像这样工作:
// These would be configurable in application.yml
currentAttempt = 0;
maxAttempts = 3;
timeoutGrowth = 2;
currentDelayTime = 5ms;
repeatNeeded = false;
while(repeatNeeded && currentAttempt < maxAttempts) {
httpStatusCode = makeRequest(someService)
if(httpStatusCode == 503) {
repeatNeeded=true;
currentAttempt++;
currentDelayTime*=timeoutGrowthRate;
sleep(currentDelayTime)
}
}
但是,通过异步调用,函数的调用者有时间做其他事情,直到 Future 有东西。我是在下面的 getObservations() 方法中编写回退代码,还是在该 getObservations() 方法的调用方中编写代码?以下是当前的调用:
public CompletableFuture getObservations(String text, Map bodyParams) 抛出 URISyntaxException {
URI uri = getUri(文本);
HttpRequest 请求 = getRequest(uri, text, bodyParams);
Map contextMap = Optional.ofNullable(MDC.getCopyOfContextMap()).orElse(Collections.emptyMap());
Instant startTime = Instant.now();
return httpClient.sendAsync(request, BodyHandlers.ofString())
.exceptionally(ex -> {
throw new ExternalToolException(externalServiceConfig.getName(), ex);
})
.thenApply(response -> {
long toolRequestDurationMillis = ChronoUnit.MILLIS.between(startTime, Instant.now());
if (HttpStatus.valueOf(response.statusCode()).is2xxSuccessful()) {
ToolResponse toolResponse = processResponse(response, toolRequestDurationMillis);
logToolResponse(toolResponse);
return toolResponse;
}
log.error("{} returned non-200 response code: {}", externalServiceConfig.getName(), response.statusCode());
throw new ExternalToolException(externalServiceConfig.getName(), response.statusCode());
});
}
如果您可以考虑使用具有非常强大 API 包括重试的响应式 java。例如,
request()
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1)));
有更多选项,例如仅针对特定异常重试或定义最大退避
request()
.retryWhen(Retry.backoff(3, Duration.ofSeconds(2)));
.maxBackoff(5)
.filter(throwable -> isRetryableError(throwable))
您可以使用 WebClient
,它是一个 non-blocking 客户端,通过底层 HTTP 客户端库(例如 Reactor Netty
公开流畅的反应式 API
webClient.get()
.uri("/api")
.retrieve()
.bodyToMono(String.class)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1)));
如果出于某种原因,你仍然想使用 HttpClient,你可以换行 CompletableFuture
Mono.fromFuture(httpClient.sendAsync(request, BodyHandlers.ofString()))
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1)));
我需要对可能失败的请求实施指数退避。但是,它是作为异步请求实现的。如果这是同步完成的,我会更好地了解将延迟放在哪里。粗略地说,我认为它会像这样工作:
// These would be configurable in application.yml
currentAttempt = 0;
maxAttempts = 3;
timeoutGrowth = 2;
currentDelayTime = 5ms;
repeatNeeded = false;
while(repeatNeeded && currentAttempt < maxAttempts) {
httpStatusCode = makeRequest(someService)
if(httpStatusCode == 503) {
repeatNeeded=true;
currentAttempt++;
currentDelayTime*=timeoutGrowthRate;
sleep(currentDelayTime)
}
}
但是,通过异步调用,函数的调用者有时间做其他事情,直到 Future 有东西。我是在下面的 getObservations() 方法中编写回退代码,还是在该 getObservations() 方法的调用方中编写代码?以下是当前的调用:
public CompletableFuture getObservations(String text, Map
Instant startTime = Instant.now();
return httpClient.sendAsync(request, BodyHandlers.ofString())
.exceptionally(ex -> {
throw new ExternalToolException(externalServiceConfig.getName(), ex);
})
.thenApply(response -> {
long toolRequestDurationMillis = ChronoUnit.MILLIS.between(startTime, Instant.now());
if (HttpStatus.valueOf(response.statusCode()).is2xxSuccessful()) {
ToolResponse toolResponse = processResponse(response, toolRequestDurationMillis);
logToolResponse(toolResponse);
return toolResponse;
}
log.error("{} returned non-200 response code: {}", externalServiceConfig.getName(), response.statusCode());
throw new ExternalToolException(externalServiceConfig.getName(), response.statusCode());
});
}
如果您可以考虑使用具有非常强大 API 包括重试的响应式 java。例如,
request()
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1)));
有更多选项,例如仅针对特定异常重试或定义最大退避
request()
.retryWhen(Retry.backoff(3, Duration.ofSeconds(2)));
.maxBackoff(5)
.filter(throwable -> isRetryableError(throwable))
您可以使用 WebClient
,它是一个 non-blocking 客户端,通过底层 HTTP 客户端库(例如 Reactor Netty
webClient.get()
.uri("/api")
.retrieve()
.bodyToMono(String.class)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1)));
如果出于某种原因,你仍然想使用 HttpClient,你可以换行 CompletableFuture
Mono.fromFuture(httpClient.sendAsync(request, BodyHandlers.ofString()))
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1)));