SpringCloud升级之路2020.0.x版-37. 实现异步的客户端封装配置管理的意义与设计

2021年11月25日 阅读数:1
这篇文章主要向大家介绍SpringCloud升级之路2020.0.x版-37. 实现异步的客户端封装配置管理的意义与设计,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

本系列代码地址:https://github.com/JoJoTec/spring-cloud-parentgit

为什么须要封装异步 HTTP 客户端 WebClient

对于同步的请求,咱们使用 spring-cloud-openfeign 封装的 FeignClient,并作了额外的定制。对于异步的请求,使用的是异步 Http 客户端即 WebClient。WebClient 使用也比较简单,举一个简单的例子即:github

//使用 WebClient 的 Builder 建立 WebClient
WebClient client = WebClient.builder()
  //指定基址
  .baseUrl("http://httpbin.org")
  //能够指定一些默认的参数,例如默认 Cookie,默认 HttpHeader 等等
  .defaultCookie("cookieKey", "cookieValue")
  .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) 
  .build();

建立好 WebClient 后便可以使用这个 WebClient 进行调用:web

// GET 请求 /anything 并将 body 转化为 String
Mono<String> stringMono = client.get().uri("/anything").retrieve().bodyToMono(String.class);
//这里为了测试,采用阻塞获取
String block = stringMono.block();

返回的结果以下所示(请求 http://httporg.bin/anything 会将请求中的全部内容原封不动返回,从这里咱们能够看出上面测试的 Header 还有 cokkie 都被返回了):spring

{
  "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"
}

咱们也能够加入负载均衡的功能,让 WebClient 利用咱们内部的 LoadBalancer,负载均衡调用其余微服务,首先注入负载均衡 Filter:编程

@Autowired
ReactorLoadBalancerExchangeFilterFunction lbFunction;

建立 WebClient 的时候,将这个 Filter 加入:json

//使用 WebClient 的 Builder 建立 WebClient
WebClient client = WebClient.builder()
  //指定基址微服务
  .baseUrl("http://微服务名称")
  //能够指定一些默认的参数,例如默认 Cookie,默认 HttpHeader 等等
  .defaultCookie("cookieKey", "cookieValue")
  .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) 
  //负载均衡器,改写url
  .filter(lbFunction)
  .build();

这样,这个 WebClient 就能调用微服务了。微信

可是,这样还不能知足咱们的需求:cookie

  1. 须要在 WebClient 加入像 Feignclient 里面加的相似的重试与断路机制,线程隔离就不须要了,由于都是异步请求不会阻塞任务线程。
  2. 须要针对不一样的微服务配置不一样的链接超时以及响应超时来适应不一样微服务。
  3. 这些配置都增长了代码的复杂度,咱们须要减小这些代码对于业务的侵入性,最好能经过纯配置实现这些 WebClient 的初始化。

要实现的配置设计以及使用举例

首先,咱们要实现的 WebClient,其 Filter 包含三个:app

  1. 重试 Filter:重试的 Filter 要在负载均衡 Filter 以前,由于重试的时候,咱们会从负载均衡器获取另外一个实例进行重试,而不是在同一个实例上重试屡次
  2. 负载均衡 Filter,其实就是内置的 ReactorLoadBalancerExchangeFilterFunction
  3. 断路器 Filter:断路器须要在负载均衡以后,由于只有负载均衡以后才能拿到具体本地调用的服务实例,这样咱们才能实现基于微服务实例方法级别的断路器

须要重试的场景:负载均衡

  1. 非 2xx 的响应码返回,而且方法是能够重试的方法。如何定义方法是能够重试的,首先 GET 方法是能够重试的,对于其余方法,根据配置中的是否配置了这个 URL 能够重试决定
  2. 异常重试
    1. 链接异常:例如链接超时,链接中断等等,全部请求的链接异常均可以重试,由于请求并无发出去。
    2. 断路器异常:该服务实例方法级别的断路器打开了,须要直接重试其余实例,由于请求并无发出去。
    3. 响应超时异常:这个重试逻辑和非 2xx 的响应码返回同样。

咱们须要实现的配置方式是,经过这样配置 application.yml

webclient:
  configs:
    //微服务名称
    testService1:
      //请求基址,第一级域名做为微服务名称
      baseUrl: http://testService1
      //最多的 http 链接数量
      maxConnection: 50
      //链接超时
      connectTimeout: 500ms
      //响应超时
      responseTimeout: 60s
      //除了 GET 方法外,哪些路径还能重试
      retryablePaths:
        - /retryable/**
        - /user/orders

加入这些配置,咱们就能获取载对应微服务的 WebClient 的 Bean,例如:

//自动装载 咱们自定义的 WebClient 的 NamedContextFactory,这个是咱们后面要实现的
@Autowired
private WebClientNamedContextFactory webClientNamedContextFactory;


//经过微服务名称,获取对应的微服务调用的 WebClient
webClientNamedContextFactory.getWebClient("testService1");

接下来,咱们会实现这些。

微信搜索“个人编程喵”关注公众号,每日一刷,轻松提高技术,斩获各类offer