Spring cloud upgrade 2020.0.x version - 37. Significance and design of asynchronous client encapsulation configuration management

Keywords: spring-cloud

Code address of this series: https://github.com/JoJoTec/sp...

Why do I need to encapsulate the asynchronous HTTP client WebClient

For synchronous requests, we use FeignClient encapsulated in spring cloud openfeign and make additional customization. For asynchronous requests, the asynchronous Http client, i.e. WebClient, is used. The use of WebClient is also relatively simple. For example:

//Create a WebClient using WebClient's Builder
WebClient client = WebClient.builder()
  //Specify base address
  .baseUrl("http://httpbin.org")
  //You can specify some default parameters, such as default Cookie, default HttpHeader, and so on
  .defaultCookie("cookieKey", "cookieValue")
  .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) 
  .build();

After creating the WebClient, you can use the WebClient to call:

// GET request / anything and convert body to String
Mono<String> stringMono = client.get().uri("/anything").retrieve().bodyToMono(String.class);
//Here, blocking acquisition is used for testing
String block = stringMono.block();

The results returned are as follows (request) http://httporg.bin/anything All contents in the request will be returned intact. From here, we can see that the Header and cokkie tested above have been returned):

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip", 
    "Cookie": "TestCookie=TestCookieValue,getAnythingCookie=getAnythingCookieValue", 
    "Getanythingheader": "getAnythingHeaderValue", 
    "Host": "httpbin.org", 
    "Testheader": "TestHeaderValue", 
    "User-Agent": "ReactorNetty/1.0.7"
  }, 
  "json": null, 
  "method": "GET", 
  "origin": "12.12.12.12", 
  "url": "http://httpbin.org/anything"
}

We can also add the function of load balancing, so that WebClient can use our internal LoadBalancer to call other microservices for load balancing, and first inject the load balancing Filter:

@Autowired
ReactorLoadBalancerExchangeFilterFunction lbFunction;

When creating a WebClient, add this Filter to:

//Create a WebClient using WebClient's Builder
WebClient client = WebClient.builder()
  //Specify base microservice
  .baseUrl("http://Microservice name ")
  //You can specify some default parameters, such as default Cookie, default HttpHeader, and so on
  .defaultCookie("cookieKey", "cookieValue")
  .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) 
  //Load balancer, rewrite url
  .filter(lbFunction)
  .build();

In this way, the WebClient can call the microservice.

However, this does not meet our needs:

  1. A similar retry and break mechanism like Feignclient needs to be added to the WebClient, so thread isolation is not required, because all asynchronous requests will not block the task thread.
  2. Different connection timeouts and response timeouts need to be configured for different microservices to adapt to different microservices.
  3. These configurations increase the complexity of the code. We need to reduce the intrusion of these codes into the business. It is best to initialize these webclients through pure configuration.

Configuration design to be implemented and use examples

First, the WebClient we want to implement has three filters:

  1. Retrying Filter: the retrying Filter should be before the load balancing Filter, because when retrying, we will obtain another instance from the load balancer for retrying, rather than retrying multiple times on the same instance.
  2. Load balancing Filter is actually the built-in ReactorLoadBalancerExchangeFilterFunction
  3. Circuit breaker Filter: the circuit breaker needs to be after load balancing, because only after load balancing can we get the specific locally invoked service instance, so that we can realize the circuit breaker based on the microservice instance method level.

Scenarios requiring retry:

  1. A non 2xx response code is returned, and the method can be retried. How to define a method that can be retried? First, the GET method can be retried. For other methods, it can be retried according to whether this URL is configured in the configuration.
  2. Exception retry:

    1. Connection exceptions: for example, connection timeout, connection interruption, etc. all requested connection exceptions can be retried because the request was not sent.
    2. Circuit breaker exception: the circuit breaker at the service instance method level is open. You need to retry other instances directly because the request is not sent.
    3. Response timeout exception: the retry logic is the same as the return of non 2xx response code.

The configuration method we need to implement is to configure application.yml as follows:

webclient:
  configs:
    //Microservice name
    testService1:
      //Request the base address and the first level domain name as the microservice name
      baseUrl: http://testService1
      //Maximum number of http connections
      maxConnection: 50
      //connection timed out
      connectTimeout: 500ms
      //Response timeout
      responseTimeout: 60s
      //In addition to the GET method, which paths can be retried
      retryablePaths:
        - /retryable/**
        - /user/orders

By adding these configurations, we can obtain the Bean that carries the WebClient corresponding to the micro service, for example:

//Automatically load the NamedContextFactory of our customized WebClient, which we will implement later
@Autowired
private WebClientNamedContextFactory webClientNamedContextFactory;


//Obtain the WebClient of the corresponding microservice call through the microservice name
webClientNamedContextFactory.getWebClient("testService1");

Next, we will implement these.

WeChat search "my programming meow" attention to the official account, daily brush, easy to upgrade technology, and capture all kinds of offer:

Posted by keldorn on Thu, 18 Nov 2021 18:09:47 -0800