【源码系列】Spring Cloud Gateway
Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0、Spring Boot2.0 和 Project Reactor 等技术开发的网关组件,旨在为微服务架构提供简单、有效和统一的 API 路由管理方式,同时提供安全性、监控/度量和限流,Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul。
能够在任何 request 属性上匹配路由(route)
Predicates 和 filters 可以作用于特定路由,并且易于编写
集成了 Hystrix Circuit Breaker(断路器)
集成了 Spring Cloud DiscoveryClient(服务发现)
请求速率限制
路径重写
网关的作用
在微服务架构中,各个服务都是独立运行来完成某个特定领域的功能,服务间通过 REST API 或 RPC 进行通信。当前端一个请求发生时,比如查看商品详情,客户端可能要调用商品服务、库存服务、评价服务等多个微服务,如果客户端直接对接各个微服务,在复杂的调用过程中存在的问题:
网关的出现可以解决这些问题,网关是微服务架构体系对外提供能力的统一接口,本质上是对请求进行转发、前置和后置的过滤:
对请求进行一次性鉴权、限流、熔断、日志
统一协议(常见 HTTP),屏蔽后端多种不同的协议
统一错误码处理
请求转发,实现内外网的隔离
通过请求分发规则,实现灰度发布
常见的 API 网关实现方案:OpenResty(Nginx+lua)、Zuul(Netflix)、Gateway(Spring)、Kong。
Spring Cloud 已经有了 Zuul,Spring 为什么重新研发了 Gateway?
Spring Cloud Gateway 配置
Gateway 配置,maven 依赖
org.springframework.cloud
spring-cloud-starter-gateway
application.yml 配置
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
uri: lb://${serviceName} # http://localhost:8080/
predicates:
- Path= /api/**
filters:
- StripPrefix=1
id - 路由唯一 ID
uri - 目标服务地址,支持普通 URL 和 (表示从注册中心获取服务的地址)
predicates - 路由条件,匹配请求 URL 判断是否执行该路由
filters - 过滤规则,包括 pre 和 post 过滤
Spring Cloud Gateway 并没有依赖 Tomcat,而是用 NettyWebServer 来启动服务监听(从启动日志可以看到)
2020-06-19T15:55:02.698+0800 [INFO] dataworks/dataworks-gateway-service o.s.b.w.e.netty.NettyWebServer
- - Netty started on port(s): 6300
Gateway 原理
Spring Cloud Gateway 依赖 Spring Boot 和 Spring Webflux 提供的 Netty runtime,启动时 Netty Server 监听指定端口,接受客户端请求。请求处理过程如下图,几个重要组成部分:
路由(Route),网关的基本组件,由 ID、目标 URI、Predicate 集合和 Filter 集合组成。
Predicate,Java 8 引入的函数式接口,提供断言(assert)功能,可以匹配 HTTP 请求中的任何内容,如果 Predicate 集合判断结果是 true,表示请求会由该 Route 进行转发。
Filter,为请求提供前置(pre)和后置(post)过滤。

Gateway 如何工作
网关的核心是 Filter 以及 Filter Chain,客户端向 Spring Cloud Gateway 发出请求,然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(pre)或之后(post)执行业务逻辑。

Gateway 应用
Predicate 路由匹配
Spring Cloud Gateway 默认提供很多 Route Predicate Factories,分别匹配 HTTP 请求的不同属性,每个 Route 支持多个 Predicate,请求必须同时满足所有的条件才被这个路由匹配。
相关类所在包路径:org.springframework.cloud.gateway.handler.predicate,常用规则如下:
指定时间规则
BeforeRoutePredicateFactory、AfterRoutePredicateFactory、BetweenRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- After=2020-06-01T24:00:00.000+08:00[Asia/Shanghai]
# 日期格式必须满足 ZonedDateTime
# - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
Cookie 匹配规则
CookieRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Cookie=mode, test # cookie 中携带 mode=test ,test 支持正则匹配
Header 匹配规则
HeaderRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Header=X-Request-Id, \d+ # header 中携带 X-Request-Id 为数字,支持正则匹配
Host 匹配规则
HostRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Host=**.host1.com,**.host2.com
# 支持多个 host 匹配,路径命名规则支持 Ant Path,* 匹配任意字符 ** 匹配任意目录 ? 匹配单字符
Method 匹配规则
MethodRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Method=GET,POST # 路由 GET 和 POST 请求
Path 匹配规则
PathRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Path= /api/**,/api/v2/** # 支持多个 Path 匹配,路径命名规则支持 Ant Path
IP 匹配规则
RemoteAddrRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- RemoteAddr=192.168.1.1/24 # 设置某个 ip 区间号段的请求才会路由
Query 匹配规则
QueryRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Query=baz # 包含了请求参数 baz的都将被匹配
Filter 路由过滤
Filter 分为前置(Pre)和后置(Post)两种类型:
Pre 类型过滤器在请求转发到后端微服务之前执行,在 Pre 过滤器链中可以进行鉴权、限流等操作。
Post 类型过滤器在请求执行完成后,将结果返回给客户端之前执行。
Spring Cloud Gateway 中有两种 Filter 实现:GatewayFilter 和 GlobalFilter。GatewayFilter 会应用到单个路由或一组路由上,GlobalFilter 会应用到所有路由上。
相关类所在包路径:org.springframework.cloud.gateway.filter,常用路由方式如下:
GatewayFilter
AddRequestParameterGatewayFilterFactory,为请求添加一个查询参数
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
filters:
- AddRequestParameter=foo, bar # 请求增加 foo=bar 这个参数
AddResponseHeaderGatewayFilterFactory,为请求的返回的 Header 中添加数据
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
filters:
- AddResponseHeader=X-Response-Foo, bar
# Response Header 添加 key=X-Response-Foo, Valuebar
RetryGatewayFilterFactory,请求重试过滤器,当后端服务不可用时,根据配置参数发起重试请求
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
filters:
- name: Retry
args:
retries: 3 # 重试次数
status: 503 # 针对 HTTP 请求返回状态码进行重试
RequestRateLimiterGatewayFilterFactory,对请求进行限流(被限流的请求会收到 Too Many Request)
由 org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter 实现,其他参数参考实现类
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
# 令牌桶的令牌填充速度,代表允许每秒执行的请求数
redis-rate-limiter.burstCapacity: 20
# 令牌桶的容量,表示每秒用户最大能够执行的请求数量
扩展,高并发限流原理:令牌桶
常用的限流算法有两种:漏桶算法和令牌桶算法。
漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,强制限制了速率,突发流量可以以一个稳定的速率进行处理。
令牌桶的思路是,有大小固定的令牌桶,以恒定的速率源源不断地产生令牌(token)。如果令牌不被消耗,或者被消耗的速度小于产生的速度,令牌就会不断地增多,直到把桶填满,再产生的令牌就会从桶中溢出。每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。
Google 开源工具包 Guava 提供了限流工具类 RateLimiter,该类基于令牌桶算法来完成限流,可以参考其实现。
漏桶算法和令牌桶算法最明显的区别是令牌桶算法允许流量一定程度的突发。令牌桶取走 token 是不需要耗费时间的,假设桶内有100个 token 时,那么可以瞬间允许 100 个请求通过,而漏桶按指定速率执行这 100 个请求,漏桶的优势是流出速率平滑。
GlobalFilter
GlobalFilter 作用与 GatewayFilter 相同,但是针对所有路由配置生效,全局过滤链的执行顺序按照 @Order 注解指定的顺序。
LoadBalancerClientFilter,用于实现负载均衡的全局过滤器
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
uri: lb://example_service
URI 配置使用 ,过滤器会识别到并将 example_service 名称解析成实际访问的主机和端口地址。
自定义 Filter
可以根据实际需求自定义过滤器,支持 GlobalFilter 和 GatewayFilter 两种
自定义 GlobalFilter
实现 GlobalFIlter 接口,框架会自动应用到所有的 Route,同时集成 Order 接口,指定执行顺序
@Component
public class SSOFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// ...
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
自定义 GatewayFilter
首先需要继承 AbstractGatewayFilterFactory
@Service
public class XXXGatewayFilterFactory extends AbstractGatewayFilterFactory {
@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;
}
}
需要注意的是:
类名必须以 GatewayFilterFactory 结尾,过滤器名字使用类名前缀
apply 方法中,chain.filter() 为 Pre 过滤,then 为 Post 过滤
配置类属性可以再 yml 中配置
该类需要注入到 IoC 容器
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
filter:
- name: XXX
args:
name: ${name}
Gateway 源码
网关初始化
启动注解:@GatewayAutoConfiguration(spring-cloud-gateway-core#org.springframework.cloud.gateway.config)
@Service
public class XXXGatewayFilterFactory extends AbstractGatewayFilterFactory {
@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 注解用于用于检查项目是否正确导入 依赖,而不是错误导入 依赖。
@GatewayLoadBalancerClientAutoConfiguration 初始化 LoadBalancerClientFilter 实现负载均衡。
@GatewayAutoConfiguration 中实现多个核心 Bean 的初始化。
Gateway 的配置参数参考 GatewayProperties.class
基本组件
Route
Route 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。
public class Route implements Ordered {
private final String id;
private final URI uri; // 路由指向的目的地 uri
private final int order; // 多个 Route 之间的排序,数值越小排序越靠前
private final AsyncPredicate predicate; // 匹配 Route 的条件
private final List gatewayFilters; // 应用于 Route 的过滤器
}
Predicate
Predicate 用于匹配请求和 Route,定义了 3 种逻辑操作方法:and/or/negate
public interface AsyncPredicate extends Function> {
default AsyncPredicate and(AsyncPredicate super T> other) {
// 两个 Predicate 同时满足
}
default AsyncPredicate negate() {
// 对 Predicate 匹配结果取反
}
default AsyncPredicate or(AsyncPredicate super T> other) {
// 两个 Predicate 只需满足其一
}
}
Filter
Filter 作用于请求代理之前或之后,最终是通过 filter chain 形成链式调用的,每个 filter 处理完 pre filter 逻辑后委派给 filter chain,filter chain 再委派给下一下 filter。
public interface GatewayFilter extends ShortcutConfigurable {
Mono filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
public interface GatewayFilterChain {
Mono filter(ServerWebExchange exchange);
}
XXXDefinition
RouteDefinition 对 Route 信息进行定义,最终会被 RouteLocator 解析成 Route(类似 BeanDefinition 和 Bean 的关系),FilterDefinition 和 PredicateDefinition 同理。
public class RouteDefinition {
private String id = UUID.randomUUID().toString();
private List predicates = new ArrayList(); // Predicate 定义、描述
private List filters = new ArrayList(); // Filter 定义、描述
private URI uri; // 路由目标 URI
}
public class FilterDefinition {
private String name; // 定义了 Filter 名称,命名规范:为对应的工厂名称前缀
private Map args = new LinkedHashMap(); // 解析配置
}
public class PredicateDefinition {
private String name; // 定义了 Predicate 名称,命名规范:为对应的工厂名称
private Map args = new LinkedHashMap(); // 解析配置
}
XXXFactory
RoutePredicateFactory 生产 Predicate 的工厂,是所有 predicate factory 的顶级接口。GatewayFilterFactory 职责就是生产 GatewayFilter。
public interface RoutePredicateFactory extends ShortcutConfigurable, Configurable {
default Predicate apply(Consumer consumer) {
}
default AsyncPredicate applyAsync(Consumer consumer) {
}
}
public interface GatewayFilterFactory extends ShortcutConfigurable, Configurable {
default GatewayFilter apply(Consumer consumer) {
}
}
RouteLocator
用于获取 Route,通过 RouteDefinitionLocator 获取到 RouteDefinition,然后转换成 Route
public interface RouteLocator {
Flux getRoutes();
}
public class RouteDefinitionRouteLocator implements RouteLocator,
BeanFactoryAware, ApplicationEventPublisherAware {
private final RouteDefinitionLocator routeDefinitionLocator;
// RoutePredicateFactory 列表
private final Map predicates = new LinkedHashMap();
// GatewayFilterFactory 列表
private final Map gatewayFilterFactories = new HashMap();
public Flux 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 predicate = this.combinePredicates(routeDefinition);
List gatewayFilters = this.getFilters(routeDefinition);
return ((AsyncBuilder)Route
.async(routeDefinition)
.asyncPredicate(predicate)
.replaceFilters(gatewayFilters))
.build();
}
}
路由匹配
Spring WebFlux 的访问入口 (对应 MVC 中的 DispatcherServlet)
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
private List handlerMappings;
private List handlerAdapters;
public Mono 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 handle(ServerWebExchange exchange, Object handler) {
WebHandler webHandler = (WebHandler)handler;
Mono mono = webHandler.handle(exchange);
return mono.then(Mono.empty());
}
}
public class FilteringWebHandler implements WebHandler {
public Mono handle(ServerWebExchange exchange) {
Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
List gatewayFilters = route.getFilters();
List combined = new ArrayList(this.globalFilters);
// 合并 GlobalFilter 和 GatewayFilter,整体排序
combined.addAll(gatewayFilters);
AnnotationAwareOrderComparator.sort(combined);
// 创建 GatewayFilterChain 处理请求
return (new FilteringWebHandler.DefaultGatewayFilterChain(combined))
.filter(exchange);
}
}
[2]
[3]
[4]