Spring Cloud Gateway

 

目录

Spring Cloud Gateway

1.     Source

2.     how it work

3.     hendler

3.1.         GatewayAutoConfiguration

3.2.         DispatcherHandler

3.3.         RoutePredicateHandlerMapping

4.     Route

4.1.         Route

4.2.         RouteDefinition

4.3.         Locator

4.4.         RouteRefreshListener

5.     Predicate

5.1.         Predicate

5.2.         PredicateFactory

6.     Filter

6.1.         GlobalFilter

6.2.         GatewayFilter

6.3.         GatewayFilterAdapter-适配器方式

6.4.         GatewayFilterSpec-工厂方式

6.5.         LoadBalancerClientFilter

7.     参考文献

 

1.  Source

Versoin  Spring Cloud Gateway 3.0.0-SNAPSHOT

 

This project provides an API Gateway built on top of the Spring Ecosystem, including: Spring 5, Spring Boot 2 and Project Reactor. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.

该项目提供了一个构建在 Spring 生态系统之上的 API 网关,包括:Spring 6Spring Boot 3 Project ReactorSpring Cloud Gateway 旨在提供一种简单而有效的方法来路由到 API,并为它们提供横切关注点,例如:安全性、监控/指标和弹性。

 

To include Spring Cloud Gateway in your project, use the starter with a group ID of org.springframework.cloud and an artifact ID of spring-cloud-starter-gateway. See the Spring Cloud Project page for details on setting up your build system with the current Spring Cloud Release Train.

 

要在项目中包含SpringCloudGateway,请使用组IDorg.springframework.Cloudstarter和工件IDSpringcloudstartergatewaystarter。有关使用当前SpringCloudRelease Train设置构建系统的详细信息,请参阅SpringCloudProject页面。

Glossary

术语汇编

Route: The basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates, and a collection of filters. A route is matched if the aggregate predicate is true.

路线:网关的基本组成部分。它由ID、目标URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则匹配路由。

Predicate: This is a Java 8 Function Predicate. The input type is a Spring Framework ServerWebExchange. This lets you match on anything from the HTTP request, such as headers or parameters.

谓词:这是一个Java8函数谓词。输入类型是Spring Framework ServerWebExchange。这允许您匹配HTTP请求中的任何内容,例如标头或参数。

Filter: These are instances of Spring Framework GatewayFilter that have been constructed with a specific factory. Here, you can modify requests and responses before or after sending the downstream request.

筛选器:这些是使用特定工厂构建的Spring Framework GatewayFilter的实例。在这里,您可以在发送下游请求之前或之后修改请求和响应。

2.  how it work

提供了 Spring Cloud Gateway 工作原理的概述:

IMG_256

Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a route, it is sent to the Gateway Web Handler. This handler runs the request through a filter chain that is specific to the request. The reason the filters are divided by the dotted line is that filters can run logic both before and after the proxy request is sent. All “pre” filter logic is executed. Then the proxy request is made. After the proxy request is made, the “post” filter logic is run.

客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 确定请求与路由匹配,则将其发送到 Gateway Web Handler。此处理程序通过特定于请求的过滤器链运行请求。过滤器被虚线分开的原因是过滤器可以在发送代理请求之前和之后运行逻辑。执行所有过滤器逻辑。然后进行代理请求。发出代理请求后,运行“post”过滤器逻辑。

 

3.  hendler

3.1.   GatewayAutoConfiguration

org.springframework.cloud.gateway.config. GatewayAutoConfiguration通过注解加载启动

@Configuration

@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)

@EnableConfigurationProperties

@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class })

@AutoConfigureAfter({ GatewayLoadBalancerClientAutoConfiguration.class,

      GatewayClassPathWarningAutoConfiguration.class })

@ConditionalOnClass(DispatcherHandler.class)

public class GatewayAutoConfiguration {

}

 

org.springframework.cloud.gateway.config. GatewayAutoConfiguration通过注解初始化各种factory

 

@Bean

   public BeforeRoutePredicateFactory beforeRoutePredicateFactory() {

      return new BeforeRoutePredicateFactory();

   }

 

   @Bean

   public BetweenRoutePredicateFactory betweenRoutePredicateFactory() {

      return new BetweenRoutePredicateFactory();

   }

 

   @Bean

   public CookieRoutePredicateFactory cookieRoutePredicateFactory() {

      return new CookieRoutePredicateFactory();

   }

 

   @Bean

   public HeaderRoutePredicateFactory headerRoutePredicateFactory() {

      return new HeaderRoutePredicateFactory();

   }

 

   @Bean

   public HostRoutePredicateFactory hostRoutePredicateFactory() {

      return new HostRoutePredicateFactory();

   }

 

org.springframework.cloud.gateway.config. GatewayAutoConfiguration通过注解初始化各种handler

 

 

 

   @Bean

   public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {

      return new FilteringWebHandler(globalFilters);

   }

 

 

 

org.springframework.cloud.gateway.config. GatewayAutoConfiguration通过注解初始化各种Listener

 

 

@Bean

   @Primary

   //TODO: property to disable composite?

   public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {

      return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));

   }

 

   @Bean

   public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {

      return new RouteRefreshListener(publisher);

   }

 

 

 

3.2.   DispatcherHandler

 

 

org.springframework.web.reactive.config. WebFluxConfigurationSupport注解启动加载DispatcherHandler

 

   @Bean

   public DispatcherHandler webHandler() {

      return new DispatcherHandler();

   }

 

 

org.springframework.web.reactive.config.DispatcherHandler初始化加载所有的HandlerMapping

 

   protected void initStrategies(ApplicationContext context) {

      Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(

             context, HandlerMapping.class, true, false);

 

      ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());

      AnnotationAwareOrderComparator.sort(mappings);

      this.handlerMappings = Collections.unmodifiableList(mappings);

 

      Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(

             context, HandlerAdapter.class, true, false);

 

      this.handlerAdapters = new ArrayList<>(adapterBeans.values());

      AnnotationAwareOrderComparator.sort(this.handlerAdapters);

 

      Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(

             context, HandlerResultHandler.class, true, false);

 

      this.resultHandlers = new ArrayList<>(beans.values());

      AnnotationAwareOrderComparator.sort(this.resultHandlers);

   }

 

 

org.springframework.web.reactive.config.DispatcherHandlerhandle方法通过HandlerMapping找到handler

 

 

@Override

   public Mono<Void> handle(ServerWebExchange exchange) {

      if (this.handlerMappings == null) {

          return createNotFoundError();

      }

      return Flux.fromIterable(this.handlerMappings)

             .concatMap(mapping -> mapping.getHandler(exchange))

             .next()

             .switchIfEmpty(createNotFoundError())

             .flatMap(handler -> invokeHandler(exchange, handler))

             .flatMap(result -> handleResult(exchange, result));

   }

 

3.3.   RoutePredicateHandlerMapping

 

 

org.springframework.cloud.gateway.handler. RoutePredicateHandlerMapping注解启动加载getHandlerInternallookupRoute找到Route

 

      @Override

   protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {

      // don't handle requests on the management port if set

      if (managmentPort != null && exchange.getRequest().getURI().getPort() == managmentPort.intValue()) {

          return Mono.empty();

      }

      exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());

 

      return lookupRoute(exchange)

             // .log("route-predicate-handler-mapping", Level.FINER) //name this

             .flatMap((Function<Route, Mono<?>>) r -> {

                exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);

                if (logger.isDebugEnabled()) {

                   logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);

                }

 

                exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);

                return Mono.just(webHandler);

             }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {

                exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);

                if (logger.isTraceEnabled()) {

                   logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");

                }

             })));

   }

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {

      return this.routeLocator

             .getRoutes()

             //individually filter routes so that filterWhen error delaying is not a problem

             .concatMap(route -> Mono

                   .just(route)

                   .filterWhen(r -> {

                      // add the current route we are testing

                      exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());

                      return r.getPredicate().apply(exchange);

                   })

                   //instead of immediately stopping main flux due to error, log and swallow it

                   .doOnError(e -> logger.error("Error applying predicate for route: "+route.getId(), e))

                   .onErrorResume(e -> Mono.empty())

             )

             // .defaultIfEmpty() put a static Route not found

             // or .switchIfEmpty()

            // .switchIfEmpty(Mono.<Route>empty().log("noroute"))

             .next()

             //TODO: error handling

             .map(route -> {

                if (logger.isDebugEnabled()) {

                   logger.debug("Route matched: " + route.getId());

                }

                validateRoute(route, exchange);

                return route;

             });

 

      /* TODO: trace logging

          if (logger.isTraceEnabled()) {

             logger.trace("RouteDefinition did not match: " + routeDefinition.getId());

          }*/

   }

 

 

通过各个RoutePredicateFactory判断路由

 

@Override

   public Predicate<ServerWebExchange> apply(Config config) {

      return exchange -> {

          List<HttpCookie> cookies = exchange.getRequest().getCookies().get(config.name);

          if (cookies == null) {

             return false;

          }

          for (HttpCookie cookie : cookies) {

             if (cookie.getValue().matches(config.regexp)) {

                return true;

             }

          }

          return false;

      };

   }

 

 

 

4.  Route

4.1.   Route

 

Route:网关的基本构建块。它由 ID、目标 URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则路由匹配。

Predicate:这是一个Java 8 函数谓词。输入类型是Spring FrameworkServerWebExchange。这使您可以匹配 HTTP 请求中的任何内容,例如标头或参数。

Filter Spring Framework GatewayFilter厂构建的实例。在这里,您可以在发送下游请求之前或之后修改请求和响应。

 

 

org.springframework.cloud.gateway.route. Route内容

 

public class Route implements Ordered {

 

   private final String id;

 

   private final URI uri;

 

   private final int order;

 

   private final AsyncPredicate<ServerWebExchange> predicate;

 

   private final List<GatewayFilter> gatewayFilters;

}

 

 

4.2.   RouteDefinition

 

org.springframework.cloud.gateway.route. RouteDefinition内容

 

public class RouteDefinition {

   @NotEmpty

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

 

   @NotEmpty

   @Valid

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

 

   @Valid

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

 

   @NotNull

   private URI uri;

 

   private int order = 0;

}

 

4.3.   Locator

 

org.springframework.cloud.gateway.route. CachingRouteLocator缓存对象

 

public class CachingRouteLocator implements RouteLocator, ApplicationListener<RefreshRoutesEvent> {

 

   private final RouteLocator delegate;

   private final Flux<Route> routes;

   private final Map<String, List> cache = new HashMap<>();

 

   public CachingRouteLocator(RouteLocator delegate) {

      this.delegate = delegate;

      routes = CacheFlux.lookup(cache, "routes", Route.class)

             .onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE));

   }

}

 

org.springframework.cloud.gateway.route. InMemoryRouteDefinitionRepository缓存对象

 

public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {

 

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

 

   @Override

   public Mono<Void> save(Mono<RouteDefinition> route) {

      return route.flatMap( r -> {

          routes.put(r.getId(), r);

          return Mono.empty();

      });

   }

 

   @Override

   public Mono<Void> delete(Mono<String> routeId) {

      return routeId.flatMap(id -> {

          if (routes.containsKey(id)) {

             routes.remove(id);

             return Mono.empty();

          }

          return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: "+routeId)));

      });

   }

 

   @Override

   public Flux<RouteDefinition> getRouteDefinitions() {

      return Flux.fromIterable(routes.values());

   }

}

 

4.4.   RouteRefreshListener

 

 

org.springframework.cloud.gateway.route. RouteRefreshListener 动态路由实现

public class RouteRefreshListener

      implements ApplicationListener<ApplicationEvent> {

 

   private HeartbeatMonitor monitor = new HeartbeatMonitor();

   private final ApplicationEventPublisher publisher;

 

   public RouteRefreshListener(ApplicationEventPublisher publisher) {

      Assert.notNull(publisher, "publisher may not be null");

      this.publisher = publisher;

   }

}

5.    Predicate

5.1.   Predicate

Route:网关的基本构建块。它由 ID、目标 URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则路由匹配。

Predicate:这是一个Java 8 函数谓词。输入类型是Spring FrameworkServerWebExchange。这使您可以匹配 HTTP 请求中的任何内容,例如标头或参数。

public class DefaultServerWebExchange implements ServerWebExchange {

   private final ServerHttpRequest request;

   private final ServerHttpResponse response;

   private final Map<String, Object> attributes = new ConcurrentHashMap<>();

   private final Mono<WebSession> sessionMono;

   private final LocaleContextResolver localeContextResolver;

   private final Mono<MultiValueMap<String, String>> formDataMono;

   private final Mono<MultiValueMap<String, Part>> multipartDataMono;

   @Nullable

   private final ApplicationContext applicationContext;

}

 

 

5.2.   PredicateFactory

Route:网关的基本构建块。它由 ID、目标 URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则路由匹配。

Predicate:这是一个Java 8 函数谓词。输入类型是Spring FrameworkServerWebExchange。这使您可以匹配 HTTP 请求中的任何内容,例如标头或参数。

The After Route Predicate Factory --时间:匹配之前

The Before Route Predicate Factory --时间:匹配之后

The Between Route Predicate Factory --匹配两个时间之间

The Cookie Route Predicate Factory --匹配cookie内容

The Header Route Predicate Factory --匹配Header 内容

The Host Route Predicate Factory--匹配Host 内容

The Method Route Predicate Factory--匹配Host 内容

The Path Route Predicate Factory --匹配Path  内容

The Query Route Predicate Factory -- 匹配参数

The RemoteAddr Route Predicate Factory --匹配远程地址

The Weight Route Predicate Factory --配置权重路由谓

 

 

org.springframework.cloud.gateway.handler.predicate .CookieRoutePredicateFactory例子

 

 

 

public class CookieRoutePredicateFactory extends AbstractRoutePredicateFactory<CookieRoutePredicateFactory.Config> {

 

   @Override

   public Predicate<ServerWebExchange> apply(Config config) {

      return exchange -> {

          List<HttpCookie> cookies = exchange.getRequest().getCookies().get(config.name);

          if (cookies == null) {

             return false;

          }

          for (HttpCookie cookie : cookies) {

             if (cookie.getValue().matches(config.regexp)) {

                return true;

             }

          }

          return false;

      };

   }

 

 

6.  Filter

6.1.   GlobalFilter

org.springframework.cloud.gateway.filter. GlobalFilter全局过滤器

 

public interface GlobalFilter {

   Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

}

6.2.   GatewayFilter

 

org.springframework.cloud.gateway.filter. GatewayFilter局部过滤器

public interface GatewayFilter extends ShortcutConfigurable {

   String NAME_KEY = "name";

   String VALUE_KEY = "value";

   Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

}

 

 

6.3.   GatewayFilterAdapter-适配器方式

 

org.springframework.cloud.gateway. handler. FilteringWebHandler. GatewayFilterAdapter局部过滤器

private static class GatewayFilterAdapter implements GatewayFilter {

 

      private final GlobalFilter delegate;

 

      public GatewayFilterAdapter(GlobalFilter delegate) {

          this.delegate = delegate;

      }

 

      @Override

      public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

          return this.delegate.filter(exchange, chain);

      }

 

      @Override

      public String toString() {

          final StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");

          sb.append("delegate=").append(delegate);

          sb.append('}');

          return sb.toString();

      }

   }

6.4.   GatewayFilterSpec-工厂方式

 

org.springframework.cloud.gateway.route.builder. GatewayFilterSpec局部过滤器

   public GatewayFilterSpec addRequestHeader(String headerName, String headerValue) {

      return filter(getBean(AddRequestHeaderGatewayFilterFactory.class)

             .apply(c -> c.setName(headerName).setValue(headerValue)));

}

 

6.5.   LoadBalancerClientFilter

 

Step 1 RibbonAutoConfiguration自动配置

 

   @Bean

   @ConditionalOnMissingBean(LoadBalancerClient.class)

   public LoadBalancerClient loadBalancerClient() {

      return new RibbonLoadBalancerClient(springClientFactory());

   }

 

RibbonClientConfiguration 自动配置(默认)

   @Bean

   @ConditionalOnMissingBean

   public ILoadBalancer ribbonLoadBalancer(IClientConfig config,

          ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,

          IRule rule, IPing ping, ServerListUpdater serverListUpdater) {

     if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {

          return this.propertiesFactory.get(ILoadBalancer.class, config, name);

      }

      return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,

             serverListFilter, serverListUpdater);

   }

 

 

BestAvailableRule:选择最小请求数的Server

RandomRule:随机选择Server

RoundRobinRule:轮询选择Server

RetryRule:根据轮询的方式重试

WeightedResponseTimeRule:根据响应时间去分配WeightWeight越高,被选择的可能性就越大

ZoneAvoidanceRule:根据Serverzone区域和可用性来轮询选择,如果只有一个zone区域,行为跟RoundRobinRule一致

AvailabilityFilteringRule:过滤掉那些因为一直连接失败(标记了circuit tripped)和高并发(activeConnections 超过配置的阈值)Server,尽量保证可用性

DynamicServerListLoadBalancer:继承于BaseLoadBalancer,它对基础负载均衡器做了扩展。在该负载均衡器,实现了服务实例清单在运行期间的动态更新;同时还具备了对服务实例清单的过滤功能,也就是说可以通过过滤器来选择获取一批服务实例清单。

 

ZoneAwareLoadBalancer:对DynamicServerListLoadBalancer的扩展。在DynamicServerListLoadBalancer中没有区分zone区域,而ZoneAwareLoadBalancer主要扩展的功能就是增加了zone区域过滤。

 

 

Step 2 LoadBalancerClientFilterfilter方法,判断LB负载均衡。

 

@Override

   public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

      URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);

      String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);

      if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {

          return chain.filter(exchange);

      }

      //preserve the original url

      addOriginalRequestUrl(exchange, url);

 

      log.trace("LoadBalancerClientFilter url before: " + url);

 

      final ServiceInstance instance = choose(exchange);

 

      if (instance == null) {

          String msg = "Unable to find instance for " + url.getHost();

          if(properties.isUse404()) {

             throw new FourOFourNotFoundException(msg);

          }

          throw new NotFoundException(msg);

      }

 

      URI uri = exchange.getRequest().getURI();

 

      // if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,

      // if the loadbalancer doesn't provide one.

      String overrideScheme = instance.isSecure() ? "https" : "http";

      if (schemePrefix != null) {

          overrideScheme = url.getScheme();

      }

 

      URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);

 

      log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);

      exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);

      return chain.filter(exchange);

   }

 

Step 3 通过LoadBalancerClient获得真是访问地址

                  final ServiceInstance instance = choose(exchange);

 

Step 4:通过RibbonLoadBalancerClientchoose方法

 

 

   /**

    * New: Select a server using a 'key'.

    */

   public ServiceInstance choose(String serviceId, Object hint) {

      Server server = getServer(getLoadBalancer(serviceId), hint);

      if (server == null) {

          return null;

      }

      return new RibbonServer(serviceId, server, isSecure(server, serviceId),

             serverIntrospector(serviceId).getMetadata(server));

   }

 

 

 

protected ILoadBalancer getLoadBalancer(String serviceId) {

      return this.clientFactory.getLoadBalancer(serviceId);

   }

 

 

Step 5:通过ILoadBalancer 的获取默认算法ZoneAwareLoadBalancer的负载均衡

具体算法参考代码:

 @Override

    protected void setServerListForZones(Map<String, List<Server>> zoneServersMap) {

        super.setServerListForZones(zoneServersMap);

        if (balancers == null) {

            balancers = new ConcurrentHashMap<String, BaseLoadBalancer>();

        }

        for (Map.Entry<String, List<Server>> entry: zoneServersMap.entrySet()) {

           String zone = entry.getKey().toLowerCase();

            getLoadBalancer(zone).setServersList(entry.getValue());

        }

        // check if there is any zone that no longer has a server

        // and set the list to empty so that the zone related metrics does not

        // contain stale data

        for (Map.Entry<String, BaseLoadBalancer> existingLBEntry: balancers.entrySet()) {

            if (!zoneServersMap.keySet().contains(existingLBEntry.getKey())) {

                existingLBEntry.getValue().setServersList(Collections.emptyList());

            }

        }

    }   

 

 

7.  参考文献

 

spring-cloud-gateway官网

https://cloud.spring.io/spring-cloud-gateway/reference/html/

 

 

Spring-Cloud-Gateway+Ribbon负载均衡

https://blog.csdn.net/htmlxxxx/article/details/115379226