При использовании оператора retry(..)
реактора WebClient
функции обменного фильтра не запускаются при повторных попытках. Я понимаю, почему, но проблема в том, что функция (как показано ниже) генерирует токен аутентификации со временем истечения. Это может произойти, пока запрос «повторяется», токен истекает, потому что функция Exchange не вызывается повторно при повторной попытке. Есть ли способ, как заново сгенерировать токен для каждой повторной попытки?
После AuthClientExchangeFunction
генерируется токен аутентификации (JWT) с истечением срока действия.
public class AuthClientExchangeFunction implements ExchangeFilterFunction {
private final TokenProvider tokenProvider;
public IntraAuthWebClientExchangeFunction(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
String jwt = tokenProvider.getToken();
return next.exchange(withBearer(request, jwt));
}
private ClientRequest withBearer(ClientRequest request, String jwt){
return ClientRequest.from(request)
.headers(headers -> headers.set(HttpHeaders.AUTHORIZATION, "Bearer "+ jwt))
.build();
}
}
Допустим, токен действителен в течение 2999 мс -> Каждый запрос повторной попытки не выполняется из-за 401.
WebClient client = WebClient.builder()
.filter(new AuthClientExchangeFunction(tokenProvider))
.build();
client.get()
.uri("/api")
.retrieve()
.bodyToMono(String.class)
.retryBackoff(1, Duration.ofMillis(3000)) ;
Редактировать Вот исполняемый пример
@SpringBootTest
@RunWith(SpringRunner.class)
public class RetryApplicationTests {
private static final MockWebServer server = new MockWebServer();
private final RquestCountingFilterFunction requestCounter = new RquestCountingFilterFunction();
@AfterClass
public static void shutdown() throws IOException {
server.shutdown();
}
@Test
public void test() {
server.enqueue(new MockResponse().setResponseCode(500).setBody("{}"));
server.enqueue(new MockResponse().setResponseCode(500).setBody("{}"));
server.enqueue(new MockResponse().setResponseCode(500).setBody("{}"));
server.enqueue(new MockResponse().setResponseCode(200).setBody("{}"));
WebClient webClient = WebClient.builder()
.baseUrl(server.url("/api").toString())
.filter(requestCounter)
.build();
Mono<String> responseMono1 = webClient.get()
.uri("/api")
.retrieve()
.bodyToMono(String.class)
.retryBackoff(3, Duration.ofMillis(1000)) ;
StepVerifier.create(responseMono1).expectNextCount(1).verifyComplete();
assertThat(requestCounter.count()).isEqualTo(4);
}
static class RquestCountingFilterFunction implements ExchangeFilterFunction {
final Logger log = LoggerFactory.getLogger(getClass());
final AtomicInteger counter = new AtomicInteger();
@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
log.info("Sending {} request to {} {}", counter.incrementAndGet(), request.method(), request.url());
return next.exchange(request);
}
int count() {
return counter.get();
}
}
}
output
MockWebServer[44855] starting to accept connections
Sending 1 request to GET http://localhost:44855/api/api
MockWebServer[44855] received request: GET /api/api HTTP/1.1 and responded: HTTP/1.1 500 Server Error
MockWebServer[44855] received request: GET /api/api HTTP/1.1 and responded: HTTP/1.1 500 Server Error
MockWebServer[44855] received request: GET /api/api HTTP/1.1 and responded: HTTP/1.1 500 Server Error
MockWebServer[44855] received request: GET /api/api HTTP/1.1 and responded: HTTP/1.1 200 OK
org.junit.ComparisonFailure:
Expected :4
Actual :1