spring-cloud-gateway作为cloud体系的一员,其设计思想非常巧妙,项目leader是spring开源主要作者之一的Spencer Gibb。
一、项目pom依赖
我们首先从gateway的dependencies查看其所依赖项,gateway本身对spring-boot-starter有依赖,gateway在微服务架构体系当中也是以Java 进程的方式而存在,这样我们在做二次元开发时可以不需要再次依赖此项;gateway-core的源码采用流表达式编码,这里要求我们的jdk保持在1.8版本以上以及对reactor异步模型有初步认知;其次对eureka的引入,默认采用eureka为服务注册中心;还有其他的引入比如redis、hystrix等均为功能功能性必要依赖。
二、工程结构解读
如图工程代码结构非常的清晰和明确,这得益于作者的优秀设计思想,采用工厂模式、门面模式等优秀的代码设计。
-
actuate
actuate 包下GatewayControllerEndpoint.java是一个内嵌endpoint监控api管理类,方便通过api来观察当前的routes配置和filter配置信息;其中有个POST(/routes/{id})可以运行时添加route配置。spring-boot和spring-cloud内嵌的管理类api的设计初衷便是为了方便与管理类平台集成,比如spring-cloud-admin。
-
config
config包下是gateway工程配置类或者项目启动初始化类, 分类和命名都非常的清晰,通过类名便可知道其中的内部作用,其包内类图如图所示:
-
GatewayAutoConfiguration.java
GatewayAutoConfiguration类从头部注解可以了解其中的类加载顺序和依赖条件。
Configuration注解是spring ioc的标准配置注入注解;spring.cloud.gateway.enabled配置默认是开启的,即只要引入了spring-cloud-gateway-starter便开启了网关;AutoConfigureBefore和AutoConfigureAfter是配置注入的前后顺序保证;ConditionalOnClass标注必须依赖的bean条件,RouteLocator、ForwardPathFilter等实例bean都是为WebFilter(DispatcherHandler) 服务的,而GatewayAutoConfiguration类则是负责RouteLocatorBuilder、RetryGatewayFilterFactory等全局单一实例注入的。这个与我们自己spring-boot项目中需要一个注入配置类的作用类似。 -
GatewayClassPathWarningAutoConfiguration.java
GatewayClassPathWarningAutoConfiguration作用比较单一,是gateway工程启动过程中的一个加载检查机制,当绑定条件丢失时会有log警告输出。
-
GatewayEnvironmentPostProcessor.java
2.2.0-SNAPSHOT版本新增的一个配置类,用于操作进程内部全局环境变量的值,作用与spring-cloud-admin可以动态修改spring-boot的日志级别类是。
-
GatewayLoadBalancerClientAutoConfiguration.java
负责LoadBalancerClientFilter(负载均衡过滤器,在下游服务分布式多实例情况下做请求负载路由)的初始化注入。
-
GatewayMetricsAutoConfiguration.java
负责GatewayMetricsFilter(请求监控全局过滤器)初始化注入。
-
GatewayNoLoaBalancerClientAutoConfiguration.java
2.2.0-SNAPSHOT新增配置类,其中声明了一个受保护的内部全局过滤器,在处理请求是没有负载均衡计算,当RibbonAutoConfiguration类缺失情况下才会启动,之前gateway工程必须依赖netflix.ribbon作为默认的负载均衡引入,在2.2.0正式版本发布之后将会取代这个限制,即默认可以不开启负载均衡。
-
GatewayProperties.java
spring.cloud.gateway这个配置项下的配置信息定义。主要包括routes、defaultFilters。
-
GatewayRedisAutoConfiguration.java
gateway的请求限流是通过redis + lua script 来实现的,此类作用不言而喻。
-
GlobalCorsProperties.java
Http Cors层面的配置,通常采用默认即可,即http请求不做限制,可以google了解一下Cors。
-
LoadBalancerProperties.java
结合负载均衡等过滤器使用,其中只有一个配置属性(private boolean use404)。
-
HttpClientProperties.java
Gateway依赖webflux,其HttpClient默认丢弃了apache的HttpClient,而是采用的reactor-netty框架封装的HttpClient,采用的是异步编程模式,HttpClientProperties则是为HttpClient而配置的。
-
PropertiesRouteDefinitionLocator.java
GatewayProperties的代理类。
-
discovery
discovery包其实跟config包作用很类似,主要是服务发现方面的配置以及服务发现逻辑实现。
-
DiscoveryLocatorProperties.java
配置实现类,srping.cloud.gateway.discovery.locator.enabled默认为false, 必须在配置文件将其配置为true,才能开启服务发现的功能;srping.cloud.gateway.discovery.locator.routeIdPrefix显式为下有路由服务添加一个前缀标识;其他配置项采用默认配置即可。
-
GatewayDiscoveryClientAutoConfiguration.java
从代码来看,主要是两个注入方法discoveryClientRouteDefinitionLocator()和discoveryLocatorProperties();
discoveryLocatorProperties()方法向IOC注入一个DiscoveryLocatorProperties类的实例,注意此方法初始化了默认的predicate和FilterDefinition,为具体的服务发现逻辑计算与转换(discoveryClient.getServices())备用。
-
DiscoveryClientRouteDefinitionLocator.java
DiscoveryClientRouteDefinitionLocator这个类可以认为是服务发现的一个代理类,其final类型的属性主要有DiscoveryClient、DiscoveryLocatorProperties,丛载实现getRouteDefinitions方法。
discoveryClient.getServices()从注册中心获取所有的服务;
默认注入的DiscoveryClient是一个代理client,其中注入了两个具体的实现类对象,分别是SimpleDicoveryClient和EurekaDiscoveryClient(这是netflix.eureka提供的实现,如果采用zk或者自己实现的注册中心作为服务发现服务需要自己实现DiscoveryClient这个接口来注入),SimpleDiscoveryClient是gateway自身进程实例的代理,eureka中的才能获取到注册中心的服务实例。
discoveryClient.getServices()方法的第84行获取了所有的serviceInstance,然后在89行开始遍历所有的serviceInstance实例,将每个serviceInstance信息转换成对应的RouteDedinition对象,依次设置serviceId和利用正则表达式将serviceInstance的服务id转换成对应的路由uri,并转换出各个服务特有的predicate和filterDefinition(注意此方法返回结果有内存缓存,会定期做过期清理,默认服务发现间隔1分钟,不会每次服务调用都会调用此方法)。
-
event
此包定义进程的事件封装,所有类都继承至ApplicationEvent抽象类,这是spring框架的内部标准,这个标准也是继承了jdk的EventObject,至于集成spring开发的应用如果需要采用事件发布订阅或者观察者模式等最好也实现该抽象类;抽象类没有抽象方法的约束,只有一个时间戳的mark字段,对具体实现没有任何影响。
-
filter
filter包是gateway的核心实现(代码解读放在后续文章)。
filter包一级目录是全局过滤器的实现,均实现了GlobalFilter接口和Ordered接口;
factory包下是基于工厂模式实现的工厂过滤器,为可配置的过滤器,可针对服务粒度进行可选配置,或者在代码中能够实现api级别的过滤;
headers包针对Http header层面的过滤器,这正常情况运用不到;
retelimit包下是基于redis+lua scriptde的限流实现。
-
handler
此包下主要是Predicate类工厂声明,在配置路由设置断言的时候常用来断言path、header、method等(源码解读放在后续文章)。
-
route
路由层的抽象与定义,这个与下游服务配置深度绑定,可以想象将一个服务实例抽象为一个RouteDefinition,然后为此route增加其predicate和filter等抽象配置。
-
support
工具包,里面有各种filter逻辑和配置初始化所用的工具类,等同于我们平常的util包。
三、结语
本篇文章旨在剖析spring-cloud-gateway-core工程下的结构,总结下来其实我觉得最大的收获是开源作者对于代码的合理设计使得包结构非常的简洁清晰,值得广大Java爱好者学习,能够深化行为和命名注释至自己的code当中;其次对于actuate、config、discovery、event简单包下的源码做了解读,后续章节将会重点专注与filter、handler和route下源码的解读,也是gateway框架架构设计精华部分。
附:基于spring-cloud-gateway增强实现参考enhance-gateway。