gpt4 book ai didi

java - Spring WebClient - 如何根据响应头延迟重试

转载 作者:行者123 更新时间:2023-12-03 11:19:34 30 4
gpt4 key购买 nike

一点背景
我一直在学习 Spring Webflux 和响应式(Reactive)编程,但遇到了一个问题,我正在尝试使用 Spring Webclient 解决重试逻辑。我创建了一个客户端并成功调用了一个返回一些 JSON 数据的外部 Web 服务 GET 端点。
问题
当外部服务以 503 - Service Unavailable 响应时状态,响应包括 Retry-After带有一个值的 header ,该值指示在重试请求之前我应该​​等待多长时间。我想在 Spring Webflux/Reactor 中找到一种方法来告诉 webClient 在 X 周期后重试它的请求,其中 X 是现在和我从响应 header 中解析出的 DateTime 之间的差异。
简单的 WebClient GET 请求

public <T> Mono<T> get(final String url, Class<T> clazz) {
return webClient
.get().uri(url)
.retrieve()
.bodyToMono(clazz);
}
网络客户端生成器
我使用构建器来创建 webClient上面方法中使用的变量,它作为实例变量存储在类中。
webClientBuilder = WebClient.builder();
webClientBuilder.codecs(clientCodecConfigurer -> {
clientCodecConfigurer.defaultCodecs();
clientCodecConfigurer.customCodecs().register(new Jackson2JsonDecoder());
clientCodecConfigurer.customCodecs().register(new Jackson2JsonEncoder());
});
webClient = webClientBuilder.build();
重试时间
我试图理解和使用 retryWhen方法与 Retry类,但无法确定我是否可以访问或传递那里的响应 header 值。
public <T> Mono<T> get(final String url, Class<T> clazz) {
return webClient
.get().uri(url)
.retrieve()
.bodyToMono(clazz);
.retryWhen(new Retry() {
@Override
public Publisher<?> generateCompanion(final Flux<RetrySignal> retrySignals) {
// Can I use retrySignals or retryContext to find the response header somehow?
// If I can find the response header, how to return a "yes-retry" response?
}
})
}
具有额外逻辑和数据库交互的过滤器
我还尝试做一些额外的逻辑并使用 WebClient.Builder 的过滤器,但这只会让我停止新请求(调用 #get ),直到之前建立的 Retry-After 值已经过去。
webClientBuilder = WebClient.builder();
webClientBuilder.codecs(clientCodecConfigurer -> {
clientCodecConfigurer.defaultCodecs();
clientCodecConfigurer.customCodecs().register(new Jackson2JsonDecoder());
clientCodecConfigurer.customCodecs().register(new Jackson2JsonEncoder());
});
webClientBuilder.filter(ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
final Clock clock = Clock.systemUTC();
final int id = (int) clientRequest.attribute("id"); // id is saved as an attribute for the request, pull it out here
final long retryAfterEpochMillis = // get epoch millisecond from DB for id
if(epoch is in the past) {
return Mono.just(clientRequest);
} else { // have to wait until epoch passes to send request
return Mono.just(clientRequest).delayElement(Duration.between(clock.instant(), Instant.ofEpochMilli(retryAfterEpochMillis)));
}
})
);
webClient = webClientBuilder.build();
.onStatus(HttpStatus::isError, response -> {
final List<String> retryAfterHeaders = response.headers().header("Retry-After");
if(retryAfterHeaders.size() > 0) {
final long retryAfterEpochMillis = // parse millisecond epoch time from header
// Save millisecond time to DB associated to specific id
}
return response.bodyToMono(String.class).flatMap(body ->
Mono.error(new RuntimeException(
String.format("Request url {%s} failed with status {%s} and reason {%s}",
url,
response.rawStatusCode(),
body))));
})
感谢任何帮助,如果我可以提供更多上下文数据来提供帮助,我会提供帮助。

最佳答案

1. 在重试构建器中检索 header

public class WebClientStatefulRetry3 {
public static void main(String[] args) {
WebClient webClient = WebClient.create();

call(webClient)
.retryWhen(Retry.indefinitely()
.filter(ex -> ex instanceof WebClientResponseException.ServiceUnavailable)
.doBeforeRetryAsync(signal -> Mono.delay(calculateDelay(signal.failure())).then()))
.block();
}

private static Mono<String> call(WebClient webClient) {
return webClient.get()
.uri("http://mockbin.org/bin/b2a26614-0219-4018-9446-c03bc1868ebf")
.retrieve()
.bodyToMono(String.class);
}

private static Duration calculateDelay(Throwable failure) {
String headerValue = ((WebClientResponseException.ServiceUnavailable) failure).getHeaders().get("Retry-After").get(0);

return // calculate delay here from header and current time;
}
}
2. 使用扩展运算符访问前一个响应并生成下一个
public class WebClientRetryWithExpand {
public static void main(String[] args) {
WebClient webClient = WebClient.create();

call(webClient)
.expand(prevResponse -> {
List<String> header = prevResponse.headers.header("Retry-After");
if (header.isEmpty()) {
return Mono.empty();
}
long delayInMillis = // calculate delay from header and current time

return Mono.delay(Duration.ofMillis(delayInMillis))
.then(call(webClient));
})
.last()
.block();
}

private static Mono<ResponseWithHeaders> call(WebClient webClient) {
return webClient.get()
.uri("https://example.com")
.exchangeToMono(response -> response.bodyToMono(String.class)
.map(rawResponse -> new ResponseWithHeaders(rawResponse, response.headers())));
}

@Data
static class ResponseWithHeaders {
private final String rawResponse;
private final ClientResponse.Headers headers;
}
}

关于java - Spring WebClient - 如何根据响应头延迟重试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65744150/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com