重试时如何重新调用WebClient的ExchangeFilterFunction

时间:2019-10-28 21:18:28

标签: spring spring-webflux spring-webclient

使用反应堆的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)) ;

修改 这是executable example

@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();
        }
    }

}

输出

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

1 个答案:

答案 0 :(得分:0)

您需要将Spring-boot版本更新为2.2.0.RELEASEretry()将不会调用以前版本中的交换功能。

我已经用一个简单的代码(在Kotlin中)对此进行了测试。

@Component
class AnswerPub {

    val webClient = WebClient.builder()
        .filter(PrintExchangeFunction())
        .baseUrl("https://jsonplaceholder.typicode.com").build()

    fun productInfo(): Mono<User> {
        return webClient
            .get()
            .uri("/todos2/1")
            .retrieve()
            .bodyToMono(User::class.java)
            .retry(2) { it is Exception }
    }

    data class User(
        val id: String,
        val userId: String,
        val title: String,
        val completed: Boolean
    )

}

class PrintExchangeFunction : ExchangeFilterFunction {
    override fun filter(request: ClientRequest, next: ExchangeFunction): Mono<ClientResponse> {
        println("Filtered")
        return next.exchange(request)
    }

}

控制台输出如下:

2019-10-29 09:31:55.912  INFO 12206 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2019-10-29 09:31:55.917  INFO 12206 --- [           main] c.e.s.SpringWfDemoApplicationKt          : Started SpringWfDemoApplicationKt in 3.19 seconds (JVM running for 4.234)
Filtered
Filtered
Filtered

因此,在我的情况下,交换函数每次都被调用。