<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Cloud Gateway 源碼剖析之Route數(shù)據(jù)模型

          共 64493字,需瀏覽 129分鐘

           ·

          2021-04-02 12:39

          點(diǎn)擊上方老周聊架構(gòu)關(guān)注我


          一、前言

          我們上一篇講了:Spring Cloud Gateway 源碼剖析之配置初始化,通過(guò)自動(dòng)加載初始化六個(gè)配置實(shí)例,Spring Cloud Gateway 就完成自身的加載和初始化工作。我們知道 Gateway 的核心是路由加過(guò)濾,既然網(wǎng)關(guān)相關(guān)初始化工作做好了,那得開始路由相關(guān)的工作了。

          接下來(lái)我們就來(lái)分析下平時(shí)在 properties 或者 yml 中配置的有關(guān) Gateway 的配置是如何構(gòu)建成 Route 的。

          二、Route 構(gòu)建方式

          一般構(gòu)建分為兩種:外部化配置和編程方式

          1、外部化配置

          spring:  cloud:    gateway:      routes:       - id: after_route // ①        uri: https://example.org // ②        predicates:         - Cookie=mycookie,mycookievalue // ③        filters:         - AddRequestHeader=X-Request-Foo, Bar // ④
          • ① 配置了一個(gè) Route id 為 after_route

          • ② 客戶端請(qǐng)求轉(zhuǎn)發(fā)的目的地:https://example.org

          • ③ 在 request 中,當(dāng)存在名字 mycookie 的 cookie 的值匹配 mycookievalue 則算成功

          • ④ 定義了一個(gè) Filter,匹配成功后,會(huì)在請(qǐng)求頭上添加 X-Request-Foo:Bar

          2、編程方式

          用上面的例子,轉(zhuǎn)換成編程方式

          @Bean
          public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
             builder.routes()
                  .route(r -> r.cookie("mycookie""mycookievalue")
                      .filters(f -> f.addRequestHeader("X-Request-Foo""Bar"))
                      .uri("https://example.org")
                  )
                  .build();
          }

          簡(jiǎn)單介紹了構(gòu)建 Route 的兩種方式,下面分析 Route 是如何把外部化配置與編碼配置之間進(jìn)行轉(zhuǎn)換。

          三、Route 構(gòu)建原理

          1、外部化配置

          外部化配置是通過(guò) GatewayProperties 進(jìn)行構(gòu)建的

          /**
           * 網(wǎng)關(guān)配置信息加載
           * 從appliccation.yml中解析前綴為spring.cloud.gateway的配置
           */

          @ConfigurationProperties("spring.cloud.gateway")
          @Validated
          public class GatewayProperties {
              private final Log logger = LogFactory.getLog(this.getClass());

              /**
               * 路由定義列表
               * 加載配置key=spring.cloud.gateway.routes 列表
               * List of Routes
               */

              @NotNull
              @Valid
              private List<RouteDefinition> routes = new ArrayList();

              /**
               * 默認(rèn)的過(guò)濾器定義列表
               * 加載配置 key = spring.cloud.gateway.default-filters 列表
               * List of filter definitions that are applied to every route.
               */

              private List<FilterDefinition> defaultFilters = new ArrayList();

              /**
               * 網(wǎng)媒體類型列表
               * 加載配置 key = spring.cloud.gateway.streamingMediaTypes 列表
               * 默認(rèn)包含{text/event-stream,application/stream+json}
               */

              private List<MediaType> streamingMediaTypes;

              public GatewayProperties() {
                  this.streamingMediaTypes = Arrays.asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON);
              }

              ...
          }

          1.1 RouteDefinition

          用來(lái)對(duì) Route 進(jìn)行定義。也就是,通過(guò) GatewayProperties 會(huì)與外部化配置進(jìn)行綁定,把外部化配置比如 properties 或者 yml 綁定到 GatewayProperties 中。

          /**
           * 路由定義實(shí)體信息,包含路由的定義信息
           */

          @Validated
          public class RouteDefinition {
              /**
               * 路由ID 編號(hào),唯一
               */

              @NotEmpty
              private String id = UUID.randomUUID().toString();

              /**
               * 謂語(yǔ)定義數(shù)組
               * predicates 屬性,謂語(yǔ)定義數(shù)組
               * 請(qǐng)求通過(guò)   判斷是否匹配。在 Route 里,PredicateDefinition 轉(zhuǎn)換成 Predicate
               */

              @NotEmpty
              @Valid
              private List<PredicateDefinition> predicates = new ArrayList();

              /**
               * 過(guò)濾器定義數(shù)組
               * filters 屬性,過(guò)濾器定義數(shù)組。
               * 在 Route 里,F(xiàn)ilterDefinition 轉(zhuǎn)換成 GatewayFilter
               */

              @Valid
              private List<FilterDefinition> filters = new ArrayList();

              /**
               * 路由指向的URI
               */

              @NotNull
              private URI uri;

              /**
               * 順序
               */

              private int order = 0;

              ...
          }

          1.2 PredicateDefinition

          /**
           * 謂語(yǔ)定義,在 Route 里,PredicateDefinition 將轉(zhuǎn)換成 Predicate
           */

          @Validated
          public class PredicateDefinition {
              /**
               * 謂語(yǔ)定義名字
               * 通過(guò) name 對(duì)應(yīng)到 org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory 的實(shí)現(xiàn)類。
               * 例如: name=Query 對(duì)應(yīng)到 QueryRoutePredicateFactory
               */

              @NotNull
              private String name;

              /**
               * 參數(shù)數(shù)組
               * 例如,name=Host / args={"_genkey_0" : "iocoder.cn"} ,匹配請(qǐng)求的 hostname 為 iocoder.cn
               */

              private Map<String, String> args = new LinkedHashMap();

              ...
          }

          1.3 FilterDefinition

          /**
           * 過(guò)濾器定義,在 Route 里,F(xiàn)ilterDefinition將轉(zhuǎn)換成 GatewayFilter
           */

          @Validated
          public class FilterDefinition {
              /**
               * 過(guò)濾器定義名字
               * 通過(guò) name 對(duì)應(yīng)到 org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory 的實(shí)現(xiàn)類。
               * 例如,name=AddRequestParameter 對(duì)應(yīng)到 AddRequestParameterGatewayFilterFactory
               */

              @NotNull
              private String name;

              /**
               * 參數(shù)數(shù)組
               * 例如 name=AddRequestParameter / args={"_genkey_0": "foo", "_genkey_1": "bar"} ,添加請(qǐng)求參數(shù) foo 為 bar
               */

              private Map<String, String> args = new LinkedHashMap();

              ...
          }

          1.4 RouteDefinitionLocator

          在上一篇文章 Spring Cloud Gateway 源碼剖析之配置初始化 中的 GatewayAutoConfiguration 核心配置類中,有提到此類。

          @Bean
          @Primary
          public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
              return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
          }

          Gateway 提供多種方式來(lái)獲取外部的配置,而 RouteDefinitionLocator 就是父接口,下面有多種實(shí)現(xiàn)。

          在這里插入圖片描述

          1.4.1 PropertiesRouteDefinitionLocator

          從配置文件(YML、Properties 等) 讀取路由配置。代碼如下:

          public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
              private final GatewayProperties properties;

              public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
                  this.properties = properties;
              }

              public Flux<RouteDefinition> getRouteDefinitions() {
                  // 從 GatewayProperties 獲取路由配置數(shù)組。
                  return Flux.fromIterable(this.properties.getRoutes());
              }
          }

          1.4.2 InMemoryRouteDefinitionRepository

          從存儲(chǔ)器(內(nèi)存、Redis、MySQL 等)讀取、保存、刪除路由配置。InMemoryRouteDefinitionRepository 是基于內(nèi)存的。

          public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
              /**
                  * 路由配置映射 通過(guò)此來(lái)保存route
               * key :路由編號(hào) {@link RouteDefinition#id}
               */

              private final Map<String, RouteDefinition> routes = Collections.synchronizedMap(new LinkedHashMap());

              public InMemoryRouteDefinitionRepository() {
              }

              public Mono<Void> save(Mono<RouteDefinition> route) {
                  return route.flatMap((r) -> {
                      this.routes.put(r.getId(), r);
                      return Mono.empty();
                  });
              }

              public Mono<Void> delete(Mono<String> routeId) {
                  return routeId.flatMap((id) -> {
                      if (this.routes.containsKey(id)) {
                          this.routes.remove(id);
                          return Mono.empty();
                      } else {
                          return Mono.defer(() -> {
                              return Mono.error(new NotFoundException("RouteDefinition not found: " + routeId));
                          });
                      }
                  });
              }

              public Flux<RouteDefinition> getRouteDefinitions() {
                  return Flux.fromIterable(this.routes.values());
              }
          }

          基于內(nèi)存,通過(guò) Map routes 來(lái)保存 Route,缺點(diǎn)是如果重啟,那么 route 會(huì)丟失。可以實(shí)現(xiàn) RouteDefinitionRepository 接口自定義比如通過(guò) Redis、Mysql 等來(lái)保存 Route。

          1.4.3 DiscoveryClientRouteDefinitionLocator

          獲取在注冊(cè)中心的服務(wù)列表,生成對(duì)應(yīng)的 RouteDefinition 數(shù)組。

          public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
              private static final Log log = LogFactory.getLog(DiscoveryClientRouteDefinitionLocator.class);
              private final DiscoveryClient discoveryClient;
              private final DiscoveryLocatorProperties properties;
              private final String routeIdPrefix;
              private final SimpleEvaluationContext evalCtxt;

              public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
                  this.discoveryClient = discoveryClient;
                  this.properties = properties;
                  if (StringUtils.hasText(properties.getRouteIdPrefix())) {
                      this.routeIdPrefix = properties.getRouteIdPrefix();
                  } else {
                      this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_";
                  }

                  this.evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();
              }

              public Flux<RouteDefinition> getRouteDefinitions() {
                  SpelExpressionParser parser = new SpelExpressionParser();
                  Expression includeExpr = parser.parseExpression(this.properties.getIncludeExpression());
                  Expression urlExpr = parser.parseExpression(this.properties.getUrlExpression());
                  Predicate includePredicate;
                  if (this.properties.getIncludeExpression() != null && !"true".equalsIgnoreCase(this.properties.getIncludeExpression())) {
                      includePredicate = (instance) -> {
                          Boolean include = (Boolean)includeExpr.getValue(this.evalCtxt, instance, Boolean.class);
                          return include == null ? false : include;
                      };
                  } else {
                      includePredicate = (instance) -> {
                          return true;
                      };
                  }
                  // 獲取discoveryClient,然后發(fā)起請(qǐng)求
                  Flux var10000 = Flux.fromIterable(this.discoveryClient.getServices());
                  DiscoveryClient var10001 = this.discoveryClient;
                  var10001.getClass();
                  return var10000.map(var10001::getInstances).filter((instances) -> {
                      return !instances.isEmpty();
                  }).map((instances) -> {
                      return (ServiceInstance)instances.get(0);
                  }).filter(includePredicate).map((instance) -> {
                      String serviceId = instance.getServiceId();
                      RouteDefinition routeDefinition = new RouteDefinition();
                      // 設(shè)置 ID
                      routeDefinition.setId(this.routeIdPrefix + serviceId);
                      // 設(shè)置 uri
                      String uri = (String)urlExpr.getValue(this.evalCtxt, instance, String.class);
                      routeDefinition.setUri(URI.create(uri));
                      ServiceInstance instanceForEval = new DiscoveryClientRouteDefinitionLocator.DelegatingServiceInstance(instance, this.properties);
                      Iterator var8 = this.properties.getPredicates().iterator();

                      Iterator var11;
                      Entry entry;
                      String value;

                      while(var8.hasNext()) {
                          PredicateDefinition originalx = (PredicateDefinition)var8.next();
                          // 添加 path 斷言
                          PredicateDefinition predicate = new PredicateDefinition();
                          predicate.setName(originalx.getName());
                          var11 = originalx.getArgs().entrySet().iterator();

                          while(var11.hasNext()) {
                              entry = (Entry)var11.next();
                              value = this.getValueFromExpr(this.evalCtxt, parser, instanceForEval, entry);
                              predicate.addArg((String)entry.getKey(), value);
                          }

                          routeDefinition.getPredicates().add(predicate);
                      }

                      var8 = this.properties.getFilters().iterator();

                      while(var8.hasNext()) {
                          FilterDefinition original = (FilterDefinition)var8.next();
                          // 添加path 重寫過(guò)濾器
                          FilterDefinition filter = new FilterDefinition();
                          filter.setName(original.getName());
                          var11 = original.getArgs().entrySet().iterator();

                          while(var11.hasNext()) {
                              entry = (Entry)var11.next();
                              value = this.getValueFromExpr(this.evalCtxt, parser, instanceForEval, entry);
                              filter.addArg((String)entry.getKey(), value);
                          }

                          routeDefinition.getFilters().add(filter);
                      }

                      return routeDefinition;
                  });
              }

              ...
          }

          可以在官方的 GatewaySampleApplication 添加 Eureka 注冊(cè)中心自行調(diào)試:

          // 開啟Eureka
          @EnableDiscoveryClient 
          public class GatewaySampleApplication {
              // ... 省略其他代碼

              @Bean
              public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
                  return new DiscoveryClientRouteDefinitionLocator(discoveryClient);
              }
          }

          當(dāng)然要自己加入 Eureka 依賴以及配置文件

          1.4.4 CachingRouteDefinitionLocator

          public class CachingRouteDefinitionLocator implements RouteDefinitionLocatorApplicationListener<RefreshRoutesEvent{
              private final RouteDefinitionLocator delegate;
              private final Flux<RouteDefinition> routeDefinitions;
              // 收集Route
              private final Map<String, List> cache = new HashMap();

              public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {
                  this.delegate = delegate;
                  FluxCacheBuilderMapMiss var10001 = CacheFlux.lookup(this.cache, "routeDefs", RouteDefinition.class);
                  RouteDefinitionLocator var10002 = this.delegate;
                  var10002.getClass();
                  this.routeDefinitions = var10001.onCacheMissResume(var10002::getRouteDefinitions);
              }

              public Flux<RouteDefinition> getRouteDefinitions() {
                  return this.routeDefinitions;
              }

              public Flux<RouteDefinition> refresh() {
                  this.cache.clear();
                  return this.routeDefinitions;
              }

              public void onApplicationEvent(RefreshRoutesEvent event) {
                  this.refresh();
              }

              /** @deprecated */
              @Deprecated
              void handleRefresh() {
                  this.refresh();
              }
          }

          1.4.5 CompositeRouteDefinitionLocator

          組合多種 RouteDefinitionLocator 的實(shí)現(xiàn),為 RouteDefinitionRouteLocator 提供統(tǒng)一入口。

          public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
              // RouteDefinitionLocator 數(shù)組
              private final Flux<RouteDefinitionLocator> delegates;

              public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates) {
                  this.delegates = delegates;
              }

              // 將組合的 delegates 的路由定義全部返回。
              public Flux<RouteDefinition> getRouteDefinitions() {
                  return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
              }
          }

          到此為止外部化的配置的多種方式全部解析完畢。接下來(lái)我們來(lái)看下編程方式。

          2、編程方式

          // org.springframework.cloud.gateway.sample.GatewaySampleApplication
          @SpringBootConfiguration
          @EnableAutoConfiguration
          @Import(AdditionalRoutesImportSelector.class)
          public class GatewaySampleApplication {

              public static final String HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS = "hello from fake /actuator/metrics/gateway.requests";

              @Value("${test.uri:http://httpbin.org:80}")
              String uri;

              public static void main(String[] args) {
                  SpringApplication.run(GatewaySampleApplication.class, args);
              }

              @Bean
              public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
                  // @formatter:off
                  // String uri = "http://httpbin.org:80";
                  // String uri = "http://localhost:9080";
                  // ① RouteLocatorBuilder 可以構(gòu)建多個(gè)路由信息。
                  return builder.routes()
                          // ② 指定了 Predicates,這里包含兩個(gè):
                          // 請(qǐng)求頭Host需要匹配**.abc.org,通過(guò) HostRoutePredicateFactory 產(chǎn)生。
                          // 請(qǐng)求路徑需要匹配/anything/png,通過(guò) PathRoutePredicateFactory 產(chǎn)生。
                          .route(r -> r.host("**.abc.org").and().path("/anything/png")
                              // ③ 指定了一個(gè) Filter,下游服務(wù)響應(yīng)后添加響應(yīng)頭X-TestHeader:foobar,
                              // 通過(guò) AddResponseHeaderGatewayFilterFactory 產(chǎn)生。
                              .filters(f ->
                                      f.prefixPath("/httpbin")
                                              .addResponseHeader("X-TestHeader""foobar"))
                               // ④ 指定路由轉(zhuǎn)發(fā)的目的地 uri。
                              .uri(uri)
                          )
                          .route("read_body_pred", r -> r.host("*.readbody.org")
                                  .and().readBody(String.class,
                                                  s -> s.trim().equalsIgnoreCase("hi"))
                              .filters(f -> f.prefixPath("/httpbin")
                                      .addResponseHeader("X-TestHeader""read_body_pred")
                              ).uri(uri)
                          )
                          .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
                              .filters(f -> f.prefixPath("/httpbin")
                                      .addResponseHeader("X-TestHeader""rewrite_request")
                                      .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
                                              (exchange, s) -> {
                                                  return Mono.just(new Hello(s.toUpperCase()));
                                              })
                              ).uri(uri)
                          )
                          .route("rewrite_request_upper", r -> r.host("*.rewriterequestupper.org")
                              .filters(f -> f.prefixPath("/httpbin")
                                      .addResponseHeader("X-TestHeader""rewrite_request_upper")
                                      .modifyRequestBody(String.class, String.class,
                                              (exchange, s) -> {
                                                  return Mono.just(s.toUpperCase() + s.toUpperCase());
                                              })
                              ).uri(uri)
                          )
                          .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
                              .filters(f -> f.prefixPath("/httpbin")
                                      .addResponseHeader("X-TestHeader""rewrite_response_upper")
                                      .modifyResponseBody(String.class, String.class,
                                              (exchange, s) -> {
                                                  return Mono.just(s.toUpperCase());
                                              })
                              ).uri(uri)
                          )
                          .route("rewrite_empty_response", r -> r.host("*.rewriteemptyresponse.org")
                              .filters(f -> f.prefixPath("/httpbin")
                                      .addResponseHeader("X-TestHeader""rewrite_empty_response")
                                      .modifyResponseBody(String.class, String.class,
                                              (exchange, s) -> {
                                                  if (s == null) {
                                                      return Mono.just("emptybody");
                                                  }
                                                  return Mono.just(s.toUpperCase());
                                              })

                              ).uri(uri)
                          )
                          .route("rewrite_response_fail_supplier", r -> r.host("*.rewriteresponsewithfailsupplier.org")
                              .filters(f -> f.prefixPath("/httpbin")
                                      .addResponseHeader("X-TestHeader""rewrite_response_fail_supplier")
                                      .modifyResponseBody(String.class, String.class,
                                              (exchange, s) -> {
                                                  if (s == null) {
                                                      return Mono.error(new IllegalArgumentException("this should not happen"));
                                                  }
                                                  return Mono.just(s.toUpperCase());
                                              })
                              ).uri(uri)
                          )
                          .route("rewrite_response_obj", r -> r.host("*.rewriteresponseobj.org")
                              .filters(f -> f.prefixPath("/httpbin")
                                      .addResponseHeader("X-TestHeader""rewrite_response_obj")
                                      .modifyResponseBody(Map.class, String.class, MediaType.TEXT_PLAIN_VALUE,
                                              (exchange, map) -> {
                                                  Object data = map.get("data");
                                                  return Mono.just(data.toString());
                                              })
                                      .setResponseHeader("Content-Type", MediaType.TEXT_PLAIN_VALUE)
                              ).uri(uri)
                          )
                          .route(r -> r.path("/image/webp")
                              .filters(f ->
                                      f.prefixPath("/httpbin")
                                              .addResponseHeader("X-AnotherHeader""baz"))
                              .uri(uri)
                          )
                          .route(r -> r.order(-1)
                              .host("**.throttle.org").and().path("/get")
                              .filters(f -> f.prefixPath("/httpbin")
                                              .filter(new ThrottleGatewayFilter()
                                              .setCapacity(1)
                                              .setRefillTokens(1)
                                              .setRefillPeriod(10)
                                              .setRefillUnit(TimeUnit.SECONDS)))
                              .uri(uri)
                          )
                          // ⑤ 創(chuàng)建RouteLocator實(shí)例
                          .build();
                  // @formatter:on
              }

              @Bean
              public RouterFunction<ServerResponse> testFunRouterFunction() {
                  RouterFunction<ServerResponse> route = RouterFunctions.route(RequestPredicates.path("/testfun"),
                          request -> ServerResponse.ok().body(BodyInserters.fromValue("hello")));
                  return route;
              }

              @Bean
              public RouterFunction<ServerResponse> testWhenMetricPathIsNotMeet() {
                  RouterFunction<ServerResponse> route = RouterFunctions
                          .route(RequestPredicates.path("/actuator/metrics/gateway.requests"), request -> ServerResponse.ok()
                                  .body(BodyInserters.fromValue(HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS)));
                  return route;
              }

              static class Hello {
                  String message;

                  Hello() {
                  }

                  Hello(String message) {
                      this.message = message;
                  }

                  public String getMessage() {
                      return message;
                  }

                  public void setMessage(String message) {
                      this.message = message;
                  }
              }

          }


          • ① 使用 Builder 模式構(gòu)建 Route

          • ② 創(chuàng)建 Predicates

          • ③ 創(chuàng)建 Filter

          • ④ 需要轉(zhuǎn)發(fā)的目的地 uri

          • ⑤ 創(chuàng)建 RouteLocator 實(shí)例

          // org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder.Builder#build
          public RouteLocator build() {
              return () -> {
                  return Flux.fromIterable(this.routes).map((routeBuilder) -> {
                      return routeBuilder.build(); // ①
                  });
              };
          }

          // RouteLocator 是 Route 集合
          public interface RouteLocator {
              Flux<Route> getRoutes();
          }

          上面 build 方法返回的是 RouteLocator 對(duì)象,它是 Route 的集合所以上面 ① 中的 build 方法就是對(duì)應(yīng)的 Route。

          public Route build() {
              Assert.notNull(this.id, "id can not be null");
              Assert.notNull(this.uri, "uri can not be null");
              AsyncPredicate<ServerWebExchange> predicate = this.getPredicate();
              Assert.notNull(predicate, "predicate can not be null");
              return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters);
          }

          下面這個(gè)就是我們最重要的 Route 數(shù)據(jù)模型了,我們來(lái)看下是如何設(shè)計(jì)的吧。

          public class Route implements Ordered {
              // id,標(biāo)識(shí)符,區(qū)別于其他 Route。
              private final String id;
              // destination uri,路由指向的目的地 uri,即客戶端請(qǐng)求最終被轉(zhuǎn)發(fā)的目的地。
              private final URI uri;
              // order,用于多個(gè) Route 之間的排序,數(shù)值越小排序越靠前,匹配優(yōu)先級(jí)越高。
              private final int order;
              // predicate,謂語(yǔ),表示匹配該 Route 的前置條件,即滿足相應(yīng)的條件才會(huì)被路由到目的地 uri。
              private final AsyncPredicate<ServerWebExchange> predicate;
              // gateway filters,過(guò)濾器用于處理切面邏輯,如路由轉(zhuǎn)發(fā)前修改請(qǐng)求頭等。
              private final List<GatewayFilter> gatewayFilters;

              ...
          }

          到這里,外部化配置的 Route、Predicate、Filter 會(huì)被映射成 RouteDefinition、FilterDefinition、PredicateDefinition,而編碼方式會(huì)被映射成 Route、AsyncPredicate 、GatewayFilter。

          有時(shí)候我們可能又有外部化配置又有編碼方式的配置,那么這時(shí)候就需要有一個(gè)轉(zhuǎn)換。那接下來(lái)就看一下 Gateway 是如何轉(zhuǎn)換的。

          四、RouteDefinitionRouteLocator

          這里將 RouteDefinitionRouteLocator 單獨(dú)作為一小節(jié)來(lái)說(shuō),RouteDefinitionRouteLocator 將外部化配置的 RouteDefinition、FilterDefinition、PredicateDefinition 轉(zhuǎn)換成 Route、AsyncPredicate、GatewayFilter。

          上一篇我們講到的核心配置類 GatewayAutoConfiguration,給我提供了分析的入口,代碼如下:

          // ① GatewayProperties 
          // ② RouteDefinitionLocator 
          @Bean
          public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> GatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, @Qualifier("webFluxConversionService") ConversionService conversionService) {
              // ③ 這里進(jìn)行轉(zhuǎn)換
              return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties, conversionService);
          }

          1、①、② 在上面外部化配置講過(guò),這里就不重復(fù)闡述了。

          2、③ 外部化配置進(jìn)行轉(zhuǎn)換,RouteDefinitionRouteLocator 是 RouteLocator 的實(shí)現(xiàn)

          public class RouteDefinitionRouteLocator implements RouteLocatorBeanFactoryAwareApplicationEventPublisherAware {
              protected final Log logger = LogFactory.getLog(this.getClass());
              public static final String DEFAULT_FILTERS = "defaultFilters";
              // RouteDefinition Locator,一個(gè) RouteDefinitionLocator 對(duì)象。
              private final RouteDefinitionLocator routeDefinitionLocator;
              private final ConversionService conversionService;
              /**
                  * predicates factories,Predicate 工廠列表,會(huì)被映射成 key 為 name, value 為 factory 的 Map。
               * 可以猜想出 gateway 是如何根據(jù) PredicateDefinition 中定義的 name 來(lái)匹配到相對(duì)應(yīng)的 factory 了。
               * key :{@link RoutePredicateFactory#name()}
               */

              private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap();
              /**
               * filter factories,Gateway Filter 工廠列表,同樣會(huì)被映射成 key 為 name, value 為 factory 的 Map。
                 * key :{@link GatewayFilterFactory#name()}
                  */

              private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap();
              // gateway properties,外部化配置類。
              private final GatewayProperties gatewayProperties;
              private final SpelExpressionParser parser = new SpelExpressionParser();
              private BeanFactory beanFactory;
              private ApplicationEventPublisher publisher;
              @Autowired
              private Validator validator;

              public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List<RoutePredicateFactory> predicates, List<GatewayFilterFactory> gatewayFilterFactories, GatewayProperties gatewayProperties, ConversionService conversionService) {
                  // 設(shè)置 RouteDefinitionLocator
                  this.routeDefinitionLocator = routeDefinitionLocator;
                  this.conversionService = conversionService;
                  // ① 初始化 RoutePredicateFactory
                  this.initFactories(predicates);
                  // ② 初始化 gatewayFilterFactories
                  gatewayFilterFactories.forEach((factory) -> {
                      GatewayFilterFactory var10000 = (GatewayFilterFactory)this.gatewayFilterFactories.put(factory.name(), factory);
                  });
                  // 設(shè)置 GatewayProperties
                  this.gatewayProperties = gatewayProperties;
              }

              public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
                  this.beanFactory = beanFactory;
              }

              public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
                  this.publisher = publisher;
              }

              // ③ 實(shí)現(xiàn) RouteLocator 的 getRoutes() 方法 獲取 route
              public Flux<Route> getRoutes() {
                  // 調(diào)用 convertToRoute 方法將 RouteDefinition 轉(zhuǎn)換成 Route。
                  return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute).map((route) -> {
                      if (this.logger.isDebugEnabled()) {
                          this.logger.debug("RouteDefinition matched: " + route.getId());
                      }

                      return route;
                  });
              }

              ...
          }

          2.1 ① 初始化 RoutePredicateFactory

          private void initFactories(List<RoutePredicateFactory> predicates) {
              predicates.forEach((factory) -> {
                  String key = factory.name();
                  if (this.predicates.containsKey(key)) {
                      this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: " + this.predicates.get(key) + ". It will be overwritten.");
                  }

                  this.predicates.put(key, factory);
                  if (this.logger.isInfoEnabled()) {
                      this.logger.info("Loaded RoutePredicateFactory [" + key + "]");
                  }

              });
          }

          2.2 ② 初始化 gatewayFilterFactories

          // FilterDefinition 轉(zhuǎn)換成 GatewayFilter
          private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
              List<GatewayFilter> filters = new ArrayList();
              // ① 處理 GatewayProperties 中定義的默認(rèn)的 FilterDefinition,轉(zhuǎn)換成 GatewayFilter。
              if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
                  filters.addAll(this.loadGatewayFilters("defaultFilters"this.gatewayProperties.getDefaultFilters()));
              }
              // ② 將 RouteDefinition 中定義的 FilterDefinition 轉(zhuǎn)換成 GatewayFilter。
              if (!routeDefinition.getFilters().isEmpty()) {
                  filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
              }
              // ③ 對(duì) GatewayFilter 進(jìn)行排序,排序的詳細(xì)邏輯請(qǐng)查閱 spring 中的 Ordered 接口。
              AnnotationAwareOrderComparator.sort(filters);
              return filters;
          }

          2.3 ③ 實(shí)現(xiàn) RouteLocator 的 getRoutes() 方法 獲取 Route,真正轉(zhuǎn)換的方法

          public Flux<Route> getRoutes() {
              // 調(diào)用 convertToRoute 方法將 RouteDefinition 轉(zhuǎn)換成 Route。
              return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute).map((route) -> {
                  if (this.logger.isDebugEnabled()) {
                      this.logger.debug("RouteDefinition matched: " + route.getId());
                  }

                  return route;
              });
          }

          private Route convertToRoute(RouteDefinition routeDefinition) {
              // 2.3.1 將 PredicateDefinition 轉(zhuǎn)換成 AsyncPredicate。
              AsyncPredicate<ServerWebExchange> predicate = this.combinePredicates(routeDefinition);
              // 2.3.2 將 FilterDefinition 轉(zhuǎn)換成 GatewayFilter。
              List<GatewayFilter> gatewayFilters = this.getFilters(routeDefinition);
              // 2.3.3 根據(jù) 1 和 2 兩步驟定義的變量生成 Route 對(duì)象。
              return ((AsyncBuilder)Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters)).build();
          }

          2.3.1 將 PredicateDefinition 轉(zhuǎn)換成 AsyncPredicate

          private AsyncPredicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
              List<PredicateDefinition> predicates = routeDefinition.getPredicates();
              // ① 調(diào)用 lookup 方法,將列表中第一個(gè) PredicateDefinition 轉(zhuǎn)換成 AsyncPredicate。
              AsyncPredicate<ServerWebExchange> predicate = this.lookup(routeDefinition, (PredicateDefinition)predicates.get(0));

              AsyncPredicate found;
              // ② 循環(huán)調(diào)用,將列表中每一個(gè) PredicateDefinition 都轉(zhuǎn)換成 AsyncPredicate。
              // ③ 應(yīng)用and操作,將所有的 AsyncPredicate 組合成一個(gè) AsyncPredicate 對(duì)象。
              for(Iterator var4 = predicates.subList(1, predicates.size()).iterator(); var4.hasNext(); predicate = predicate.and(found)) {
                  PredicateDefinition andPredicate = (PredicateDefinition)var4.next();
                  found = this.lookup(routeDefinition, andPredicate);
              }

              return predicate;
          }

          2.3.2 將 FilterDefinition 轉(zhuǎn)換成 GatewayFilter

          // FilterDefinition 轉(zhuǎn)換成 GatewayFilter
          private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
              List<GatewayFilter> filters = new ArrayList();
              // ① 處理 GatewayProperties 中定義的默認(rèn)的 FilterDefinition,轉(zhuǎn)換成 GatewayFilter。
              if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
                  filters.addAll(this.loadGatewayFilters("defaultFilters"this.gatewayProperties.getDefaultFilters()));
              }
              // ② 將 RouteDefinition 中定義的 FilterDefinition 轉(zhuǎn)換成 GatewayFilter。
              if (!routeDefinition.getFilters().isEmpty()) {
                  filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
              }
              // ③ 對(duì) GatewayFilter 進(jìn)行排序,排序的詳細(xì)邏輯請(qǐng)查閱 spring 中的 Ordered 接口。
              AnnotationAwareOrderComparator.sort(filters);
              return filters;
          }

          2.3.3 根據(jù) 2.3.1 和 2.3.2 兩步驟定義的變量生成 Route 對(duì)象

          public Route build() {
              Assert.notNull(this.id, "id can not be null");
              Assert.notNull(this.uri, "uri can not be null");
              AsyncPredicate<ServerWebExchange> predicate = this.getPredicate();
              Assert.notNull(predicate, "predicate can not be null");
              return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters);
          }

          到這里我們就已經(jīng)知道外部化配置配合是如何轉(zhuǎn)換的,那么現(xiàn)在 Route 已經(jīng)組裝完畢了,現(xiàn)在就是看一下 Route 里面的 Predicate 和 Filter 的實(shí)現(xiàn)。

          五、總結(jié)

          • Route 構(gòu)建方式有兩種方式:外部化配置和編程方式。

          • 通過(guò) RouteDefinitionLocator 的各種實(shí)現(xiàn),來(lái)多樣化的獲取不同的外置配置。

          • 既有外部化配置又有編碼方式的配置,那么這時(shí)候就需要有一個(gè)轉(zhuǎn)換根據(jù) RouteDefinitionRouteLocator 把這些外置配置轉(zhuǎn)存成 Route。




          歡迎大家關(guān)注我的公眾號(hào)【老周聊架構(gòu)】,Java后端主流技術(shù)棧的原理、源碼分析、架構(gòu)以及各種互聯(lián)網(wǎng)高并發(fā)、高性能、高可用的解決方案。

          喜歡的話,點(diǎn)贊、再看、分享三連。



          點(diǎn)個(gè)在看你最好看



          瀏覽 230
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲成人MV | 国产精品自产拍在线 | 婷婷国产成人精品一区二区 | 欧美 日韩 中文 | 羽月希奶水一区二区三区 |