Spring Cloud Gateway解析

2021-12-09 20:49:48 浏览数 (1)

Gateway( 网关),顾名思义,是出现在系统边界上的一个面向API或应用服务的、串行集中式的强管控服务,这里我们讨论的边界可以基于企业IT系统的边界,当然,可以理解为企业级应用防火墙,其目标主要起到隔离外部访问与内部系统交互的作用。在微服务概念的流行之前,网关就已经诞生了,在面向SOA体系中已经成熟,然而,随着微服务体系的快速发展,更进一步将Gateway推向更高的浪口。与其说网关催生了微服务体系,不如说微服务体系拥抱了网关。

随着微服务架构概念的提出,API 网关成为了微服务架构的一个标配组件,无时无刻在我们的应用系统架构中起着举足轻重的作用,首先,我们来了解下基于Spring Cloud微服务体系网关的架构图,具体如下所示:

当我们谈论起网关,这玩意到底有什么用?具备哪些功能?具体我们可以参考以下模型图,具体:

如上图所示:作为网关,其该具备的最基本的四大功能为:统一接入,流量管控,协议适配转发以及安全防护等。

基于上述,我们分享了网关的历史以及其具备的基本功能,现在我们了解下在不同语言体系环境下所采用的网关技术,目前市面上或者业务场景落地解决方案中,常见的开源网关大致上按照语言分类有如下几类,具体如下图所示:

若按照使用范围、成熟度以及落地场景等来划分,目前主流网关技术应用涉及以下4 种:OpenResty、Kong、Zuul/Zuul 2、Spring Cloud Gateway,此外,随着Go语言在微服务领域的快速崛起以及应用,fagongzi API 网关最近也获得不少关注。因作者当前公司主要技术栈为Java,故本文重点以Spring Cloud Gateway网关为主,解析其基本原理以及在业务中的应用。

在Spring 的早期框架中,我们基本上用的是第一代Zuul,随着Spring 5的出现,Spring Cloud 开始完善其生态,引入多种不同的组件以支撑其在微服务体系领域中的地位。因此,Spring Cloud Gateway应运而生。

Spring Cloud Gateway 是Spring Cloud 生态全新项目,其基于 Spring 5.0、Spring Boot2.0 和 Project Reactor 等技术开发的网关组件,旨在为微服务架构提供简单、有效和统一的 API 路由管理方式,同时提供安全性、监控/度量和限流,Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul。于是,大家肯定在想,这哥们疯了,Zuul都2代了,还搞Gateway ?有意思吗?答案:当然。主要基于以下几点:

  • 提供更简单、高效的 API 网关。
  • Zuul 1.x 采用 Thread per connection 方式处理请求(每个请求一个线程进行处理),

一旦服务响应慢,线程会被阻塞不释放,存在性能瓶颈。

  • 虽然 Zuul 2.x 是适合高并发的版本,但是在 Zuul 2.x 开源前 Spring 团队启动了

Gateway。

现在,我们来看下Spring Cloud Gateway的基本配置,具体如下所示,

pom.xml中的Maven依赖,具体可参考:

代码语言:javascript复制
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

application.yml 配置,具体可参考:

代码语言:javascript复制
spring:
  cloud:
    gateway:
      routes:
        - id: ${serviceId}
          uri: lb://${serviceName}    # http://localhost:8080/
          predicates:
            - Path= /api/**
          filters:
            - StripPrefix=1
  • id - 路由唯一 ID。
  • uri - 目标服务地址,支持普通 URL 和 lb://${服务名称}(表示从注册中心获取服务的

地址)。

  • predicates - 路由条件,匹配请求 URL 判断是否执行该路由。
  • filters - 过滤规则,包括 pre 和 post 过滤等相关规则。

除此之外,我们还可以通过Java Bean来进行配置,具体如下所示:

代码语言:javascript复制
@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("path_route2", r -> r.path("/user/getByUsername")
                        .uri("http://localhost:8201/user/getByUsername"))
                .build();
    }
}

现在,我们继续了解Spring Cloud Gateway 的基本原理,我们先看下其架构图,具体如下所示:

基于上述拓扑,我们可以看到:Spring Cloud Gateway 依赖 Spring Boot 和 Spring Webflux 提供的 Netty runtime,启动时 Netty Server 监听指定端口,接受客户端请求。请求处理过程如下图,几个重要组成部分:

1、路由(Route),网关的基本组件,由 ID、目标 URI、Predicate 集合和 Filter 集合组成。

2、Predicate,Java 8 引入的函数式接口,提供断言(assert)功能,可以匹配 HTTP 请求中的任何内容,如果 Predicate 集合判断结果是 true,表示请求会由该 Route 进行转发。

3、Filter,为请求提供前置(pre)和后置(post)过滤。

现在,我们来了解下其工作流原理,基本的处理流程及架构图如下:

基于上述处理图,我们可以得知:

1、Gateway接受客户端请求;

2、网关处理程序映射确定请求与路由匹配,匹配成功则将其发送到网关Web处理程序;

3、Web处理程序处理程序通过特定于请求的过滤器链运行请求:请求经过 Filter 过滤器链,执行 pre 处理逻辑,如修改请求头信息等;发出代理请求,请求被转发至下游服务并返回响应。

4、响应经过 Filter 过滤器链,执行 post 处理逻辑。

5、向客户端响应应答。

再此,我们看下Spring Cloud Gateway源码实现,以便能够更清晰地去熟悉其内部具体实现细节,具体如下所示:

网关初始化,启动注解:@GatewayAutoConfiguration(spring-cloud-gateway

-core#org.springframework.cloud.gateway.config),源码如下:

代码语言:javascript复制
@Service
public class XXXGatewayFilterFactory extends AbstractGatewayFilterFactory<XXXConfig> {
  
    @Override
    public GatewayFilter apply(final XXXConfig config) {
        return (((exchange, chain) -> {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // ...
            }));
        }));
    }

    @Data
    public static class XXXConfig {

        private String name;
    }
}

Spring Cloud Gateway 基于 Spring WebFlux 实现,@GatewayClassPathWarningAutoConfiguration 注解用于用于检查项目是否正确导入 spring-boot-starter-webflux 依赖,而不是错误导入 spring-boot-starter-web 依赖。

@GatewayLoadBalancerClientAutoConfiguration 初始化 LoadBalancerClientFilter 实现负载均衡。

@GatewayAutoConfiguration 中实现多个核心 Bean 的初始化。Gateway 的配置参数参考 GatewayProperties.class。其基本组件简要源码实现如下:

Route,作为Gateway 中最基本的组件之一,表示一个具体的路由信息载体。源码如下所示:

代码语言:javascript复制
public class Route implements Ordered {
    private final String id;
    private final URI uri;        // 路由指向的目的地 uri
    private final int order;      // 多个 Route 之间的排序,数值越小排序越靠前
    private final AsyncPredicate<ServerWebExchange> predicate;    // 匹配 Route 的条件
    private final List<GatewayFilter> gatewayFilters;              // 应用于 Route 的过滤器
}

Predicate 组件用于匹配请求和 Route,其总共定义3 种逻辑操作方法:and/or/negate。源码如下所示:

代码语言:javascript复制
public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
    default AsyncPredicate<T> and(AsyncPredicate<? super T> other) {
        // 两个 Predicate 同时满足
    }

    default AsyncPredicate<T> negate() {
        // 对 Predicate 匹配结果取反
    }

    default AsyncPredicate<T> or(AsyncPredicate<? super T> other) {
        // 两个 Predicate 只需满足其一
    }
}

Filter 组件功能主要用于请求代理之前或之后,最终通过 filter chain 形成链式进行调用,每个filter处理完pre filter逻辑后委派给 filter chain,其再委派给下一个filter。具体源码如下:

代码语言:javascript复制
public interface GatewayFilter extends ShortcutConfigurable {

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

public interface GatewayFilterChain {
    Mono<Void> filter(ServerWebExchange exchange);
}

接下来,我们再看下RouteLocator,其主要用于获取 Route,通过 RouteDefinitionLocator 获取到 RouteDefinition,然后转换成Route,源码如下所示:

代码语言:javascript复制
public interface RouteLocator {
    Flux<Route> getRoutes();
}

public class RouteDefinitionRouteLocator implements RouteLocator, 
        BeanFactoryAware, ApplicationEventPublisherAware {
  
    private final RouteDefinitionLocator routeDefinitionLocator;
    // RoutePredicateFactory 列表
    private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap();
    // GatewayFilterFactory 列表
    private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap();
                                                      
    public Flux<Route> getRoutes() {
        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) {
        AsyncPredicate<ServerWebExchange> predicate = this.combinePredicates(routeDefinition);
        List<GatewayFilter> gatewayFilters = this.getFilters(routeDefinition);
        return ((AsyncBuilder)Route
                    .async(routeDefinition)
                    .asyncPredicate(predicate)
                    .replaceFilters(gatewayFilters))
                .build();
    }
}

路由匹配部分,Spring WebFlux 的访问入口 org.springframework.web.reactive.DispatcherHandler(对应 MVC 中的 DispatcherServlet),具体源码可参考如下:

代码语言:javascript复制
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
    private List<HandlerMapping> handlerMappings;
    private List<HandlerAdapter> handlerAdapters;

    public Mono<Void> handle(ServerWebExchange exchange) {
        // 顺序使用 handlerMappings 获得对应的 WebHandler,invoke 执行
        return Flux.fromIterable(this.handlerMappings)
            // RoutePredicateHandlerMapping
            .concatMap((mapping) -> { return mapping.getHandler(exchange); })
            .next()
            .switchIfEmpty(this.createNotFoundError())
            // SimpleHandlerAdapter
            .flatMap((handler) -> { return this.invokeHandler(exchange, handler); })
            .flatMap((result) -> { return this.handleResult(exchange, result); });
    }
}

RoutePredicateHandlerMapping 匹配路由,源码如下:

代码语言:javascript复制
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
    private final FilteringWebHandler webHandler;
    private final RouteLocator routeLocator;
   
    // mapping.getHandler(exchange); 会调用至此
    protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
        return this.lookupRoute(exchange)    // 匹配 Route
            .flatMap((r) -> {
                // ...
                return Mono.just(this.webHandler);
            })
            .switchIfEmpty(/**未匹配到 Route**/);
    }
}

最后,我们在了解下Filter chain,SimpleHandlerAdapter 循环执行 WebHandler,我们以 FilteringWebHandler 为例,创建 GatewayFilterChain 处理请求,具体源码如下:

代码语言:javascript复制
public class SimpleHandlerAdapter implements HandlerAdapter {
    public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
        WebHandler webHandler = (WebHandler)handler;
        Mono<Void> mono = webHandler.handle(exchange);
        return mono.then(Mono.empty());
    }
}

public class FilteringWebHandler implements WebHandler {
    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);
        // 合并 GlobalFilter 和 GatewayFilter,整体排序
        combined.addAll(gatewayFilters);
        AnnotationAwareOrderComparator.sort(combined);
      
        // 创建 GatewayFilterChain 处理请求
        return (new FilteringWebHandler.DefaultGatewayFilterChain(combined))
          .filter(exchange);
    }
}

至此,Spring Cloud Gateway基本解析到此为止,大家有什么问题或者建议,欢迎随时留言沟通。

0 人点赞