Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0、Spring Boot2.0 和 Project Reactor 等技术开发的网关组件,旨在为微服务架构提供简单、有效和统一的 API 路由管理方式,同时提供安全性、监控/度量和限流,Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul。
Spring Cloud Gateway 提供的特性(官网):
能够在任何 request 属性上匹配路由(route)
Predicates 和 filters 可以作用于特定路由,并且易于编写
集成了 Hystrix Circuit Breaker(断路器)
集成了 Spring Cloud DiscoveryClient(服务发现)
请求速率限制
路径重写
网关的作用
在微服务架构中,各个服务都是独立运行来完成某个特定领域的功能,服务间通过 REST API 或 RPC 进行通信。当前端一个请求发生时,比如查看商品详情,客户端可能要调用商品服务、库存服务、评价服务等多个微服务,如果客户端直接对接各个微服务,在复杂的调用过程中存在的问题:
客户端需要发起多次请求,增加网络通信成本和客户端处理的复杂性
服务的鉴权会分布在每个微服务中,存在重复鉴权
微服务提供的接口协议不同(REST、RPC),客户端要对不同的协议进行适配
网关的出现可以解决这些问题,网关是微服务架构体系对外提供能力的统一接口,本质上是对请求进行转发、前置和后置的过滤:
常见的 API 网关实现方案:OpenResty(Nginx+lua)、Zuul(Netflix)、Gateway(Spring)、Kong。
Spring Cloud 已经有了 Zuul,Spring 为什么重新研发了 Gateway?
替代 Zuul 提供简单高效的 API 网关
Zuul 1.x 采用 thread per connection 方式处理请求(每个请求一个线程进行处理),一旦服务响应慢,线程会被阻塞不释放,性能有一定瓶颈
虽然 Zuul 2.x 是适合高并发的版本,但是在 Zuul 2.x 开源前 Spring 团队启动了 Gateway
Spring Cloud Gateway 配置
Gateway 配置,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 过滤
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
Header 匹配规则
HeaderRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Header=X-Request-Id, \d+
Host 匹配规则
HostRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Host=**.host1.com,**.host2.com
Method 匹配规则
MethodRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Method=GET,POST
Path 匹配规则
PathRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Path= /api/**,/api/v2
IP 匹配规则
RemoteAddrRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- RemoteAddr=192.168.1.1/24
Query 匹配规则
QueryRoutePredicateFactory 实现
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
predicates:
- Query=baz
Filter 路由过滤
Filter 分为前置(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
AddResponseHeaderGatewayFilterFactory,为请求的返回的 Header 中添加数据
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
filters:
- AddResponseHeader=X-Response-Foo, bar
RetryGatewayFilterFactory,请求重试过滤器,当后端服务不可用时,根据配置参数发起重试请求
spring:
cloud:
gateway:
routes:
- id: ${serviceId}
filters:
- name: Retry
args:
retries: 3
status: 503
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:
URI 配置使用 lb://
,过滤器会识别到并将 example_service 名称解析成实际访问的主机和端口地址。
自定义 Filter
可以根据实际需求自定义过滤器,支持 GlobalFilter 和 GatewayFilter 两种
自定义 GlobalFilter
实现 GlobalFIlter 接口,框架会自动应用到所有的 Route,同时集成 Order 接口,指定执行顺序
@Component
public class SSOFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
自定义 GatewayFilter
首先需要继承 AbstractGatewayFilterFactory
@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:
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<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
Route 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。
public class Route implements Ordered {
private final String id;
private final URI uri;
private final int order;
private final AsyncPredicate<ServerWebExchange> predicate;
private final List<GatewayFilter> gatewayFilters;
}
Predicate
Predicate 用于匹配请求和 Route,定义了 3 种逻辑操作方法:and/or/negate
public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
default AsyncPredicate<T> and(AsyncPredicate<? super T> other) {
}
default AsyncPredicate<T> negate() {
}
default AsyncPredicate<T> or(AsyncPredicate<? super T> other) {
}
}
Filter
Filter 作用于请求代理之前或之后,最终是通过 filter chain 形成链式调用的,每个 filter 处理完 pre filter 逻辑后委派给 filter chain,filter chain 再委派给下一下 filter。
public interface GatewayFilter extends ShortcutConfigurable {
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
public interface GatewayFilterChain {
Mono<Void> filter(ServerWebExchange exchange);
}
XXXDefinition
RouteDefinition 对 Route 信息进行定义,最终会被 RouteLocator 解析成 Route(类似 BeanDefinition 和 Bean 的关系),FilterDefinition 和 PredicateDefinition 同理。
public class RouteDefinition {
private String id = UUID.randomUUID().toString();
private List<PredicateDefinition> predicates = new ArrayList();
private List<FilterDefinition> filters = new ArrayList();
private URI uri;
}
public class FilterDefinition {
private String name;
private Map<String, String> args = new LinkedHashMap();
}
public class PredicateDefinition {
private String name;
private Map<String, String> args = new LinkedHashMap();
}
XXXFactory
RoutePredicateFactory 生产 Predicate 的工厂,是所有 predicate factory 的顶级接口。GatewayFilterFactory 职责就是生产 GatewayFilter。
public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {
default Predicate<ServerWebExchange> apply(Consumer<C> consumer) {
}
default AsyncPredicate<ServerWebExchange> applyAsync(Consumer<C> consumer) {
}
}
public interface GatewayFilterFactory<C> extends ShortcutConfigurable, Configurable<C> {
default GatewayFilter apply(Consumer<C> consumer) {
}
}
RouteLocator
用于获取 Route,通过 RouteDefinitionLocator 获取到 RouteDefinition,然后转换成 Route
public interface RouteLocator {
Flux<Route> getRoutes();
}
public class RouteDefinitionRouteLocator implements RouteLocator,
BeanFactoryAware, ApplicationEventPublisherAware {
private final RouteDefinitionLocator routeDefinitionLocator;
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap();
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) {
return Flux.fromIterable(this.handlerMappings)
.concatMap((mapping) -> { return mapping.getHandler(exchange); })
.next()
.switchIfEmpty(this.createNotFoundError())
.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;
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
return this.lookupRoute(exchange)
.flatMap((r) -> {
return Mono.just(this.webHandler);
})
.switchIfEmpty();
}
}
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);
combined.addAll(gatewayFilters);
AnnotationAwareOrderComparator.sort(combined);
return (new FilteringWebHandler.DefaultGatewayFilterChain(combined))
.filter(exchange);
}
}
[1] https://spring.io/projects/spring-cloud-gateway
[2] https://cloud.spring.io/spring-cloud-gateway/2.2.x/reference/html/
[3] 《SpringCloudAlibaba微服务原理与实战》
[4] http://www.iocoder.cn/categories/Spring-Cloud-Gateway/
评论