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依赖,具体可参考:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
application.yml 配置,具体可参考:
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来进行配置,具体如下所示:
@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),源码如下:
@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 中最基本的组件之一,表示一个具体的路由信息载体。源码如下所示:
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。源码如下所示:
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。具体源码如下:
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,源码如下所示:
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),具体源码可参考如下:
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 匹配路由,源码如下:
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 处理请求,具体源码如下:
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基本解析到此为止,大家有什么问题或者建议,欢迎随时留言沟通。