SpringCloud Gateway网关
# Gateway 网关
Spring Cloud Gateway 网关 组件核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对 应的服务。
补充说明&优点
- Gateway 替换了 Netflix Zuul 的一套解决方案
- 隐藏 服务的 IP/端口 等信息
- 提供统一的API路由管理方式 (url管理)
- Gateway 核心功能 过滤/路由/断言
- Gateway 也是个微服务,需要注册到 Eureka
# 核心功能
Route
(路由) :路由信息由 ID、目标URL、一组断言、一组Filter 组成 (一般情况会通过断言进行判断 路由匹配)Predicate
(断言) :定义匹配的 HTTP 中的任何信息(如:请求头/参数等...Filter
(过滤器) :可在 请求/响应 进行一些业务上的处理
# 工作流程
说明:
客户端 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 的微服务中进行处理,此时处理运行特定的请求过滤器链
过滤器虚线分开的原因:会在发送代理请求的 请求/响应 执行逻辑(所有 pre 过滤器逻辑先执行,然后执行 代理请求;代理请求完成后,执行 post 过滤器逻辑,进行响应)
示例
代码在 01Spring Cloud (opens new window) 的基础上进行编辑
创建新Maven工程(无骨架) 我创建的工程名称 gateway
gateway 依赖配置
pom.xml
<!-- gateway --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!-- eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
gateway 启动类
@SpringBootApplication @EnableDiscoveryClient // Eureka客户端 public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
gateway 配置
application.yml
server : port : 10010 spring : application : name : api-gateway cloud : gateway : routes : # 路由id (随意) - id : user-service-route # 代理服务的地址 # uri: http://localhost:9091 # 方式1 固定代理 uri : lb://user-service # 方式2 动态代理 # 路由断言,可以配置的映射路径 predicates: - Path=/user/** eureka : client : service-url : defaultZone : http://localhost:10086/eureka instance : # 优先应用ip,并非 host prefer-ip-address : true
注意:
- 填写路由断言的路径 不能存在空格
- Path=/user/**
说明:
- 代理路径 http://localhost:10010/user/1 (opens new window) ==> http://localhost:9091/user/1 (opens new window)
- 代理路由 有两种方式写,但一般不会写死,因此应用第二种方式
- 路由配置中uri所用的协议为lb时,gateway 将使用 LoadBalancerClient 把 user-service 通过 eureka 解析为 实际IP 和 端口,并进行ribbon负载均衡
- 填写路由断言的路径 不能存在空格
测试
- 依次打开 Eureka、server、gateway 三个服务
- 访问 http://localhost:10010/user/1 (opens new window)(返回 数据代表成功)
# 过滤器
通过以上工作流程可得知 ,Gateway 的路由 过滤器允许 修改传入 请求/响应 的HTTP,只作用于特定的路由
过滤器生命周期
Spring Cloud Gateway 的 Filter 有两种: pre
(请求被执行前调用)、post
(请求被执行后调用) ,以上的工作流程可看出
使用场景
- 请求鉴权:过滤器链式 调用到 filter()方法 前,如果发现无权限,返回Null
- 异常处理:过滤器链式 调用到 filter()方法 后,记录异常并返回
- 服务调用时长统计:过滤器链式 调用到 filter()方法 前后,根据时长统计
常用过滤器工厂
Gateway 中有很多内置的过滤器工厂,我们只需了解常用的过滤器
过滤器工厂名称 | 说明 |
---|---|
AddRequestHeader | 对匹配上的请求加上Header(请求头) |
RemoveRequestHeader | 对匹配上的请求去除Header(请求头) |
AddRequestParameter | 对匹配上的请求路由添加参数 |
SetStatus | 设置 HTTP请求 的响应码 |
AddResponseHeader | 对从网关返回的响应添加Header(响应头) |
StripPrefix | 对匹配上的请求路径去除前缀 |
PrefixPath | 对匹配上的请求路径添加前缀 |
更多详细参考 官网链接 (opens new window)
过滤器示例
AddRequestHeader 添加请求头
符合规则匹配成功的请求,将添加 X-Request-Foo:bar
请求头,将其传递到后端服务中
spring :
application :
name : api-gateway
cloud :
gateway :
routes :
- id : user-service-route
uri : lb://user-service
predicates:
- Path=/user/**
filters:
# 参数:请求头,值
- AddRequestHeader=X-Request-Foo,Bar
RemoveRequestHeader 移除请求头
可以在请求转发到后端服务之前进行 Header 的移除操作
spring :
application :
name : api-gateway
cloud :
gateway :
routes :
- id : user-service-route
uri : lb://user-service
predicates:
- Path=/user/**
filters:
# 参数:移除请求头 X-Request-Foo
- RemoveRequestHeader=X-Request-Foo
AddRequestParameter 请求添加参数
符合规则匹配成功的请求,将会附加 MyName=Sans
参数
spring :
application :
name : api-gateway
cloud :
gateway :
routes :
- id : user-service-route
uri : lb://user-service
predicates:
- Path=/user/**
filters:
# 参数:名称,值 (类似于 K , V)
- AddRequestParameter=MyName,Sans
SetStatus 响应返回码
用于设置 HTTP请求 的响应码 ; 支持 整数/枚举 (org.springframework.http.HttpStatus
类 查看枚举)
spring :
application :
name : api-gateway
cloud :
gateway :
routes :
- id : user-service-route
uri : lb://user-service
predicates:
- Path=/user/**
filters:
- SetStatus=401
AddResponseHeader 添加响应头
网关响应添加响应头
spring :
application :
name : api-gateway
cloud :
gateway :
default-filters:
- AddResponseHeader=X-Request-Foo,Bar
StripPrefix 去除路由前缀
对请求地址前缀去除后,再作为 代理地址
spring :
application :
name : api-gateway
cloud :
gateway :
routes :
# 路由id (随意)
- id : user-service-route
# 代理服务的地址
# uri: http://localhost:9091 # 方式1
uri : lb://user-service # 方式2
# 路由断言,可以配置的映射路径
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
···
如: http://localhost:10010/api/user/1 (opens new window) ==> http://localhost:9091/user/1 (opens new window) (去除/api)
PrefixPath 添加路由前缀
对请求地址添加前缀后,再作为 代理地址
spring :
application :
name : api-gateway
cloud :
gateway :
routes :
# 路由id (随意)
- id : user-service-route
# 代理服务的地址
# uri: http://localhost:9091 # 方式1
uri : lb://user-service # 方式2
# 路由断言,可以配置的映射路径
predicates:
- Path=/**
filters:
- PrefixPath=/user
···
如: http://localhost:10010/1 (opens new window) ==> http://localhost:9091/user/1 (opens new window) (添加/user)
测试可自行在以下的自定义过滤器工厂测试检查属性变化
# 自定义局部过滤器
Spring Cloud Gateway 过滤器工厂 ,在请求传递的过程中,对请求进行 编辑/验证。(应用场景一般有:身份验证、选择集群中指定的微服务 等...
示例
说明:
- 自定义过滤器工厂需要继承
AbstractGatewayFilterFactory
类,重写apply()
方法 , 且过滤器类命名以GatewayFilterFactory
为后缀- 测试 请求参数 在控制台展示
创建 自定义局部过滤器 (我创建的MyParamGatewayFilterFactory
package com.sans.gateway.filter; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.List; @Component public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> { /**构造函数*/ public MyParamGatewayFilterFactory() { super(Config.class); } /** 初始化 * 读取配置文件中的参数,赋值到配置类中 * */ @Override public List<String> shortcutFieldOrder() { //一个参数 return Arrays.asList("params"); } /**过滤器逻辑*/ @Override public GatewayFilter apply(Config config) { // 实例化 GatewayFilter.filter() // exchange - 请求属性 // chain - 提供一种委托给下一个过滤器的方法 return (exchange , chain) -> { // 前置 ServerHttpRequest request = exchange.getRequest(); // 路由包含 name参数 则执行 if (request.getQueryParams().containsKey(config.params)) { request.getQueryParams().get(config.params).forEach((v)->{ System.out.printf("局部过滤器 => %s = %s\n",config.params,v); }); } /**读取过滤器配置的参数 (以对象形式存储*/ public static class Config { // 对应配置在 application.yml 配置文件中的过滤参数 private String params; public String getParams() { return params; } public void setParams(String params) { this.params = params; } } }
配置
application.yml
添加 自定义过滤器spring : cloud : gateway : routes : # 路由id (随意) - id : user-service-route # 代理服务的地址 # uri: http://localhost:9091 # 方式1 uri : lb://user-service # 方式2 # 路由断言,可以配置的映射路径 predicates: - Path=/user/** filters: # 添加自定义过滤器 - MyParam=name
说明:
- 在配置中 过滤器指定可省略
GatewayFilterFactory
后缀 - 以上对应参数是
shortcutFieldOrder()
方法 返回指定参数数量的List (以上我指定返回一个参数params 的List)
- 在配置中 过滤器指定可省略
测试
- 依次打开 Eureka、server、gateway 三个服务
- 访问 http://localhost:10010/user/1?name=Sans (opens new window)
访问后,控制台会打印,若参数名name不匹配,则不打印
# 自定义全局过滤器
全局过滤器作用于所有路由, 无需其他配置!!!一般情况用于权限统一验证,安全验证等功能...
示例
说明:
- 全局过滤器 需要实现 GlobalFilter接口
- 过滤器顺序 需要实现 Ordered接口 指定数值(值越小越快
- 测试 请求是否授权 。判断是否包含
token
的请求头
创建 自定义全局过滤器 (我创建的MyGlobalFilter
package com.sans.gateway.filter; import org.apache.commons.lang.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class MyGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange , GatewayFilterChain chain) { System.out.println("=================全局过滤器MyGlobalFilter================="); String token = exchange.getRequest().getHeaders().getFirst("token"); if (StringUtils.isBlank(token)) { // 设置响应状态码为未授权 401 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } /**设置过滤器优先顺序*/ @Override public int getOrder() { return 1; } }
测试 (由于需要为请求添加请求头)
- 依次打开 Eureka、server、gateway 三个服务
- 通过 以下的IDEA内置的HTTP测试 以下有示例
说明: 请求成功会 返回数据 ;请求失败会返回401 (未授权)
IDEA内置HTTP调试
创建测试 HTTP请求 草稿文件
设置请求url测试
有示例查看使用方式等功能...
# 跨域配置
跨域:当一个请求URL的 协议
/域名
/端口
之间任意一个与当前页面URL不同,称为跨域
当前 URL | 被请求URL | 是否跨域 | 原因 | 比较 |
---|---|---|---|---|
http://www.test.com/ | http://www.test.com/index.html | N | - | - |
http://www.test.com/ | https://www.test.com/index.html | Y | 协议不同 | http/https |
http://www.test.com/ | http://www.baidu.com/ | Y | 主域名不同 | test/baidu |
http://www.test.com/ | http://blog.test.com/ | Y | 子域名不同 | www/blog |
http://www.test.com:8080/ | http://www.test.com:7001/ | Y | 端口号不同 | 8080/7001 |
这一跨域问题可通过 Gateway 网关服务器中配置即可。配置如下:
spring :
application :
name : api-gateway
cloud :
gateway :
globalcors :
cors-configurations :
# 代表所有访问到网关服务器上的地址
'[/**]' :
# 允许指定服务器地址访问
# allowedOrigins : * # *代表全部支持访问
allowedOrigins :
- "http://demo.sans.top"
allowedMethods :
- GET
上述配置 允许来自 http://demo.sans.top 的 GET请求 获取服务数据
官方具体了解:CORS Configuration (spring.io) (opens new window)
# 负载均衡&熔断器
Gateway 默认集成 Ribbon、Hystrix (配置策略也如此)。如有其它策略配置可点击一下链接了解相关配置
- Spring Cloud Feign (opens new window)
- Spring Cloud Hystrix_熔断器 (opens new window)
- Spring Cloud Ribbon_负载均衡 (opens new window)
# Gateway与Feign的区别
Gateway | Feign | |
---|---|---|
接口入口 | 统一服务入口 | 单一微服务入口 |
接口安全 | 接口隐蔽应用 | 接口暴露应用 |
路由处理 | 可以处理 | 固有。不能处理 |
应用场景 | 权限检查、控制流量 | 微服务内部调用更方便 |
仓库代码 : https://gitee.com/Bozhu12/spring-cloud-examples.git (opens new window)