Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的高性能响应式网关,由 Spring Cloud 官方提供支持。本文对 API 网关和 Spring Cloud Gateway 的概念做了简单介绍,并从快速开始、路由谓词工厂配置、网关过滤器工厂配置、流控降级方案等方面介绍该技术的应用方式。
API 网关介绍
在微服务系统中,网关就是把各个服务对外的 API 汇聚起来,让外界看起来是一个统一的接口,同时可以在网关中提供一些额外功能。简单来说,网关是所有项目的统一入口。
网关功能
- 关注稳定和安全
- 流量控制
- 日志统计
- 防止 SQL 注入
- 防止 Web 攻击
- 屏蔽工具扫描
- 黑白名单
- 证书/加解密处理
- 提供服务
- 流量控制
- 服务降级、熔断
- 负载均衡、灰度策略
- 服务过滤、聚合、发现
- 权限验证、用户等级策略
- 业务规则与参数校验
- 多级缓存
网关组成
- 路由转发
- 过滤器
网关解决方案
- Spring Cloud Netflix Zuul
- Spring Cloud Gateway
Spring Cloud Gateway
Spring Cloud Gateway 是 Spring Cloud 的二级子项目,提供了微服务网关功能,包含权限安全、监控/指标等功能。
Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式网关,不能部署在传统的 servlet 容器中工作,也不能构建成 war 包。
核心概念
- 路由(Route):
一个路由是由 ID、URI、Predicate 集合、Filter 集合组成的。 - 断言/谓词(Predicate):
断言就是一些附加条件和内容,用以匹配请求头和参数等信息。 - 过滤器(Filter):
过滤器是对请求/相应的任意处理,Spring Cloud Gateway 已经定义好 25 种过滤器,可以满足绝大多数需求。
主要架构
Gateway 快速入门
-
导入依赖:
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies>
-
配置
application.yml
:server: port: 8888 spring: application: name: gateway-demo cloud: gateway: routes: # 是一个 RouteDefinition 的 list,用 - 标识一个对象,或者用 , 分隔 - id: order_route # 路由的唯一标识,路由到订单服务 uri: http://localhost:8080/ # 目标服务的地址 predicates: Path=/order-service/** # 断言,匹配请求的 URL filters: # 过滤器,可以理解为过滤器链,每个过滤器都可以对请求进行处理 - StripPrefix=1 # 去掉请求路径的前缀,StripPrefix=1 表示去掉前缀为 /order-service 的路径 # 通过上面的配置,将 http://localhost:8888/order-service/order/1 # 转发到 http://localhost:8080/order/1
整合 Nacos
-
导入依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency>
-
配置
application.yml
:server: port: 8888 spring: application: name: gateway-demo cloud: gateway: routes: # 是一个 RouteDefinition 的 list,用 - 标识一个对象,或者用 , 分隔 - id: order_route # 路由的唯一标识,路由到订单服务 uri: lb://order-service # 目标服务的地址,使用 lb 负载均衡,需要在服务端和调用端都引入 loadbalancer predicates: Path=/order-service/** # 断言,匹配请求的 URL filters: # 过滤器,可以理解为过滤器链,每个过滤器都可以对请求进行处理 - StripPrefix=1 # 去掉请求路径的前缀,StripPrefix=1 表示去掉前缀为 /order-service 的路径 # 通过上面的配置,将 http://localhost:8888/order-service/order/1 # 转发到 http://localhost:8080/order/1 nacos: discovery: server-addr: 127.0.0.1:8848 # username: # password:
或者,使用简写形式,启用自动识别服务(基于注册中心),很简便,但不够灵活。
server: port: 8888 spring: application: name: gateway-demo cloud: gateway: discovery: locator: enabled: true # 启用自动发现服务 nacos: discovery: server-addr: 127.0.0.1:8848 # username: # password:
Spring Cloud Gateway 的配置
路由谓词工厂(Route Predicate Factories)配置
这里介绍的路由谓词工厂详细解释可以参考 官方文档。
基于 Datetime 的谓词工厂
根据时间(ZonedDatetime)做判断,主要包含:
-
AfterRoutePredicateFactory
:接收一个日期参数,判断请求日期是否晚于指定日期。predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver]
-
BeforeRoutePredicateFactory
:接收一个日期参数,判断请求日期是否早于指定日期。predicates: - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
-
BetweenRoutePredicateFactory
:接收两个日期参数,判断请求日期是否在指定时间段内。predicates: - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
基于 Cookie 的谓词工厂
predicates:
- Cookie=chocolate,ch.p # 这里支持正则表达式
基于请求头的谓词工厂
predicates:
- Header=X-Request-Id,\d+
基于主机名的谓词工厂
predicates:
- Host=**.somehost.org,**.anotherhost.org
基于请求方式的谓词工厂
predicates:
- Method=GET,POST
基于路径的谓词工厂
predicates:
- Path=/red/{segment},/blue/{segment} # 这里的 {segment} 会提取值到 segment 变量中供工厂使用
基于查询参数的谓词工厂
predicates:
- Query=green # 匹配参数包含 green 的请求
predicates:
- Query=red,gree. # 匹配参数包含 red 且值匹配 gree. 的请求
基于 IP 的谓词工厂
predicates:
- RemoteAddr=192.168.1.1/24 # 匹配 IP + 掩码指定的所有地址
基于权重的谓词工厂
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8 # 权重 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2 # 权重 2
自定义谓词工厂
Spring Cloud Gateway 允许开发者自定义谓词工厂,要求:
- 必须作为 Spring Bean 被实例化
- 命名必须为
XXXRoutePredicateFactory
- 必须实现
AbstractRoutePredicateFactory
- 必须声明静态内部类,声明属性接收配置
关于自定义谓词工厂的实现,可以参考
org.springframework.cloud.gateway.handler.predicate
包下的实现。
网关过滤器工厂(Gateway Filter Factory)配置
由于网关过滤器工厂数量很多,建议查看 官方文档,这里只给出几个常用的过滤器。
更多过滤器可以查看:org.springframework.cloud.gateway.filter.factory
。
添加请求头
filters:
- AddRequestHeader=X-Request-red, blue
filters:
- AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-2:green
添加请求参数
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- AddRequestParameter=foo, bar-{segment}
添加响应头
filters:
- AddResponseHeader=X-Response-Red, Blue
添加前缀
filters:
- PrefixPath=/mall-order # 转发时添加前缀,对应的微服务需要配置 context-path
重定向
filters:
- RedirectTo=302, https://acme.org
自定义过滤器
Spring Cloud Gateway 允许开发者自定义网关过滤器,要求:
- 必须作为 Spring Bean 被实例化
- 命名必须为
XXXGatewayFilterFactory
- 必须实现
AbstractGatewayFilterFactory
- 必须声明静态内部类,声明属性接收配置
关于自定义网关过滤器工厂的实现,可以参考
org.springframework.cloud.gateway.filter.factory
包下的实现。
全局过滤器(Global Filter)配置
与网关过滤器工厂不同,全局过滤器(Global Filter)对全局生效,是通过配置 Bean 生效的,不需要针对请求分别配置。Spring Cloud Gateway 已经提供了多种全局过滤器,具体配置方式详见 官方文档。
自定义全局过滤器
Spring Cloud Gateway 允许开发者自定义全局过滤器,要求:
- 必须作为 Spring Bean 被实例化
- 命名必须为
XXXFilter
- 必须实现
GlobalFilter
Reactor Netty 访问日志
要启用 Reactor Netty 访问日志,需要设置 -Dreactor.netty.http.server.accessLogEnabled=true
。
注意:它必须是 Java 环境变量,而不是 Spring Boot 应用配置。
logback 配置
CORS 跨域配置
跨域资源共享(Cross-Origin Resource Sharing,CROS)指的是一种基于 HTTP 头的机制,通过允许浏览器访问服务器标示的其他源的资源,为了安全起见,CORS 默认关闭。在 Spring Cloud Gateway 可以通过以下方式配置 CORS:
spring:
cloud:
gateway:
globalcors: # 跨域配置
cors-configurations:
'[/**]': # 允许跨域访问的资源
allowedOrigins: "https://docs.spring.io" # 允许跨域访问的来源
allowedMethods: # 允许的方法
- GET
除了配置全局的 CORS,Spring Cloud Gateway 允许使用路由 CORS,通过以下方式进行配置:
spring:
cloud:
gateway:
routes:
- id: cors_route
uri: https://example.org
predicates:
- Path=/service/**
metadata:
cors
allowedOrigins: '*'
allowedMethods:
- GET
- POST
allowedHeaders: '*'
maxAge: 30
整合 sentinel 流控降级
Sentinel 官网:「Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件」。
-
在 官网发布页 下载
sentinel-dashboard-x.y.z.jar
,并运行java -jar sentinel-dashboard-x.y.z.jar
默认绑定端口 8080,用户名 sentinel,密码 sentinel,此时控制台空空如也:
-
导入依赖
<!-- sentinel 整合 gateway --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> </dependency> <!-- sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
-
配置 sentinel 控制台
spring: cloud: sentinel: transport: dashboard: 127.0.0.1:8858
-
启动项目,再次打开控制台,可以看到网关模块已经被加载:
注意:由于 sentinel 是懒加载的,需要先对网关发送一次请求,才能加载出信息。
-
通过控制台的请求链路-流控选项设置流控规则:
也可以通过
GatewayRuleManager.loadRules()
配置流控规则。 -
通过控制台的请求链路-降级选项设置降级规则:
-
可以通过
GatewayCallbackManager.setBlockHandler()
配置异常返回(统一返回体):
网关的高可用
只需要部署多台实例,并使用 nginx 反向代理、负载均衡即可。