SpringCloud源码探析(五)-网关Gateway的使用

1.概述

在微服务架构中,网关提供了统一的对外访问入口(自身跨一个或多个服务),它保证了内部服务对外暴露的合理性与安全性、降低了服务之间访问的复杂性,是微服务架构中至关重要的一部分。在SpringCloud中网关主要包含两种:Zuul和Gateway,Zuul是基于Servlet实现的,属于阻塞式编程,而SpringCloud Gateway是基于Spring 5中提供的WebFlux,属于响应式编程的实现,具有更好的性能。本文将详细分析SpringCloud Gateway的使用以及其实现原理。

2.Gateway使用

2.1 网关作用

在微服务架构中,网关所处的位置如下图所示:
SpringCloud源码探析(五)-网关Gateway的使用_第1张图片
网关的主要功能如下:

(1)权限校验:校验用户身份,合法用户允许访问;
(2)路由转发:制定路由规则,根据路由映射到指定服务;
(3)请求限流:控制访问流量,当请求数量达到一定值时,限制部分访问;
(4)API监控:监控应用程序请求结果、请求数量等。

网关的优势在于:
(1)统一入口:为全部微服务提供一个唯一的入口,网关起到外部和内部的隔离作用,保障了后台服务的安全性;
(2)鉴权校验:识别每个请求的权限,拒绝不符合条件的请求;
(3)动态路由:将请求路由到不同的后台微服务中;
(4)减少耦合:减少客户端与服务端的耦合,服务可以独立发展,通过网关来做映射。

2.2 Gateway路由核心元素

SpringCloud源码探析(五)-网关Gateway的使用_第2张图片
Gateway在启动时会创建Netty Server,它会接收来自客户端的请求,收到请求后会根据路由规则匹符合条件的路由,请求会被该路由配置的过滤器依次进行拦截处理,再由Netty Client转移到服务目标,由微服务处理后返回,返回结果会再次被过滤器处理,最后才会返回给Client。这里涉及Route的核心概念如下:

  • Route:一个Gateway服务可以配置多个Route,一个Route由ID、转发URI和多个Predicates 、Filters 构成。处理请求时会按照优先级排序,找到第一个满足所有Predicates 的 Route;
  • Predicate:路由断言,判断请求是否符合要求,符合则转发到路由目的地(能够根据多种属性匹配:请求路径、方法、请求头header等)。一个Route可以包含多个Predicates;
  • Filter:过滤器包括了处理请求和响应的逻辑,可以分为 pre 和 post 两个阶段,PreFilter(请求前处理)可以做参数校验、流量监控、日志记录、修改请求内容等等,在PostFilter(请求后处理)可以做响应内容修改。Gateway 包括两类 Filter:全局 Filter、路由Filter。每种全局 Filter 全局只会有一个实例,会对所有的 Route 都生效。路由 Filter 是针对 Route进行配置的,不同的 Route 可以使用不同的参数,因此会创建不同的实例。

2.3 Gateway使用

2.3.1 引入pom

       <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>

2.3.2 添加配置

spring:
  cloud:
    gateway:
      routes:
        - id: userservice
          uri: lb://userservice
          predicates:
            - Path=/user/**
        - id: orderservice
          uri: lb://orderservice
          predicates:
            - Path=/order/**

2.3.3 启动类

@Slf4j
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        log.info("这是新的GatewayApplication");
        SpringApplication.run(GatewayApplication.class, args);
    }
}

2.3.4 运行结果

SpringCloud源码探析(五)-网关Gateway的使用_第3张图片
SpringCloud源码探析(五)-网关Gateway的使用_第4张图片

2.4 Gateway运行原理

2.4.1 路由原理

SpringCloud源码探析(五)-网关Gateway的使用_第5张图片
Gateway接收请求的流程如上图所示:

1.请求会先经过DispatcherHandler;
2.DispatcherHandler会根据RoutePredicateHandlerMapping获取Handler方法;
3.RoutePredicateHandlerMapping需要根据RouteLocator获取所有路由配置信息并匹配满足条件的路由;
4.RoutePredicateHandlerMapping会将请求交给FilteringWebHandler处理;
5.FilteringWebHandler从已经匹配的路由中获取对应的路由Filter,与全局Filter合并构造GatewayFilterChain,最终由GatewayFilterChain里的Filter按照顺序进行处理。

2.4.2 RouteLocator

一个Route主要包含以下几个属性:

属性名称 含义
Id 路由Id,具有唯一性
uri 转发请求的目标地址
Order 优先级
Predicate 规则(匹配条件),多个规则会合并成一个聚合条件
Filters 路由过滤器,这些过滤器与全局过滤器一起,按顺序处理请求并按要求返回结果
Metadata 额外的元数据

Gateway主要通过接口RouteLocator接口来获取路由配置,RouteLocator代码如下:

public interface RouteLocator {

	Flux<Route> getRoutes();

}

在Gateway的源码中,RouteLocator的实现类主要有:RouteDefinitionRouteLocator、CompositeRouteLocator、CachingRouteLocator。它们之间的关系如下图所示:
SpringCloud源码探析(五)-网关Gateway的使用_第6张图片

RouteDefinitionLocator 提供RouteDefinition ,由 RouteDefinition 构造路由。可以存在多个RouteLocator ,这些实例提供的路由最终会由 CompositeRouteLocator 整合,再由 CachingRouteLocator 缓存。CachingRouteLocator 会把下层的 RouteLocator 返回的路由缓存起来,后续直接返回使用,同时监听RefreshRoutesEvent来定时刷新缓存的路由。

2.4.3 Predicate

Predicate接口提供了可以针对请求条件的匹配规则,匹配成功的路由负责处理该请求。路由是分优先级的,匹配时会根据优先级返回第一个匹配成功的路由,配置时匹配条件越具体(越详尽)的路由应配置更高的优先级,优先级从-2^31 到 2^31-1,值越小优先级越高。Gateway提供了很多基于Predicate的规则,比如Header、Host、Parameter、Request Method等,也可以实现接口RoutePredicateFactory自定义规则,RoutePredicateFactory接口代码如下:

@FunctionalInterface
public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {
	// ...
   
  Predicate<ServerWebExchange> apply(C config);

  // ...
}

2.4.4 Filter

Gateway过滤器分为两种,全局过滤器和路由过滤器,全局 Filter会自动对所有的路由都生效,有些功能比如全局日志、请求时长等就比较适合设计成全局过滤器,这类过滤器对应的是 GlobalFilter 接口,自定义全局过滤器只需要实现该接口并注册到spring容器即可。

public interface GlobalFilter {

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

}

路由Filter是针对某个具体的路由进行配置,需要实现接口 GatewayFilterFactory,路由Filter主要针对具体路由进行不同配置,比如添加删减请求头参数、添加鉴权信息等。代码如下:

@FunctionalInterface
public interface GatewayFilterFactory<C> extends ShortcutConfigurable, Configurable<C> {
	// ...
	
	GatewayFilter apply(C config);

	// ...
}

上述两类 Filter 在处理请求之前会先放一起排序,通过Order注解或者实现 Ordered 接口声明进行声明,Gateway Filter 处理请求和响应相关的核心代码在 FilteringWebHandler 类,代码如下:

public class FilteringWebHandler implements WebHandler {
    protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class);
    private final List<GatewayFilter> globalFilters;

    public FilteringWebHandler(List<GlobalFilter> globalFilters) {
        this.globalFilters = loadFilters(globalFilters);
    }

    private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
        return (List)filters.stream().map((filter) -> {
            FilteringWebHandler.GatewayFilterAdapter gatewayFilter = new FilteringWebHandler.GatewayFilterAdapter(filter);
            if (filter instanceof Ordered) {
                int order = ((Ordered)filter).getOrder();
                return new OrderedGatewayFilter(gatewayFilter, order);
            } else {
                return gatewayFilter;
            }
        }).collect(Collectors.toList());
    }

    public Mono<Void> handle(ServerWebExchange exchange) {
        Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        List<GatewayFilter> gatewayFilters = route.getFilters();
        List<GatewayFilter> combined = new ArrayList(this.globalFilters);
        combined.addAll(gatewayFilters);
        AnnotationAwareOrderComparator.sort(combined);
        if (logger.isDebugEnabled()) {
            logger.debug("Sorted gatewayFilterFactories: " + combined);
        }

        return (new FilteringWebHandler.DefaultGatewayFilterChain(combined)).filter(exchange);
    }

    private static class GatewayFilterAdapter implements GatewayFilter {
        private final GlobalFilter delegate;

        GatewayFilterAdapter(GlobalFilter delegate) {
            this.delegate = delegate;
        }

        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return this.delegate.filter(exchange, chain);
        }

        public String toString() {
            StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
            sb.append("delegate=").append(this.delegate);
            sb.append('}');
            return sb.toString();
        }
    }

    private static class DefaultGatewayFilterChain implements GatewayFilterChain {
        private final int index;
        private final List<GatewayFilter> filters;

        DefaultGatewayFilterChain(List<GatewayFilter> filters) {
            this.filters = filters;
            this.index = 0;
        }

        private DefaultGatewayFilterChain(FilteringWebHandler.DefaultGatewayFilterChain parent, int index) {
            this.filters = parent.getFilters();
            this.index = index;
        }

        public List<GatewayFilter> getFilters() {
            return this.filters;
        }

        public Mono<Void> filter(ServerWebExchange exchange) {
            return Mono.defer(() -> {
                if (this.index < this.filters.size()) {
                    GatewayFilter filter = (GatewayFilter)this.filters.get(this.index);
                    FilteringWebHandler.DefaultGatewayFilterChain chain = new FilteringWebHandler.DefaultGatewayFilterChain(this, this.index + 1);
                    return filter.filter(exchange, chain);
                } else {
                    return Mono.empty();
                }
            });
        }
    }
}

由上述代码可知, FilteringWebHandler 会将所有的 GlobalFilter 实例加载进来并使用GatewayFilterAdapter 适配成 GatewayFilter。在handle()方法处理请求时,会将适配后的GlobalFilter 以及路由GatewayFilter合并在一个List中,根据Order进行排序,排序之后会构造一个GatewayFilterChain,由其中 filter() 方法触发这些 Filter 的执行。

3.小结

1.Gateway包含的核心元素主要有:Route、Predicate、Filter;
2.Gateway中路由过滤器分为全局过滤器和路由过滤器,全局过滤器和路由过滤器会根据Order排序执行;
3.Gateway主要通过接口RouteLocator接口来获取路由配置。

4.参考文献

1.https://www.bilibili.com/video/BV1LQ4y127n4
2.https://juejin.cn/post/6844903788680052750
3.https://spring.io/projects/spring-cloud-gateway

5.附录

https://gitee.com/Marinc/nacos.git

你可能感兴趣的:(spring,cloud,gateway,java)