1.概述
在微服务架构中,网关提供了统一的对外访问入口(自身跨一个或多个服务),它保证了内部服务对外暴露的合理性与安全性、降低了服务之间访问的复杂性,是微服务架构中至关重要的一部分。在SpringCloud中网关主要包含两种:Zuul和Gateway,Zuul是基于Servlet实现的,属于阻塞式编程,而SpringCloud Gateway是基于Spring 5中提供的WebFlux,属于响应式编程的实现,具有更好的性能。本文将详细分析SpringCloud Gateway的使用以及其实现原理。
2.Gateway使用
2.1 网关作用
在微服务架构中,网关所处的位置如下图所示:
网关的主要功能如下:
(1)权限校验:校验用户身份,合法用户允许访问;
(2)路由转发:制定路由规则,根据路由映射到指定服务;
(3)请求限流:控制访问流量,当请求数量达到一定值时,限制部分访问;
(4)API监控:监控应用程序请求结果、请求数量等。
网关的优势在于:
(1)统一入口:为全部微服务提供一个唯一的入口,网关起到外部和内部的隔离作用,保障了后台服务的安全性;
(2)鉴权校验:识别每个请求的权限,拒绝不符合条件的请求;
(3)动态路由:将请求路由到不同的后台微服务中;
(4)减少耦合:减少客户端与服务端的耦合,服务可以独立发展,通过网关来做映射。
2.2 Gateway路由核心元素
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 运行结果
2.4 Gateway运行原理
2.4.1 路由原理
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。它们之间的关系如下图所示:
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