文章目录
Spring Cloud Gateway
Gateway是Spring Cloud 第二代网关,第一代是Zuul。
Spring Cloud Gateway 简介
Spring Cloud Gateway 是什么
Spring Cloud Gateway是Spring官网基于Spring 5.0、 Spring Boot 2.0、Project Reactor等技术开发等网关
。
Spring Cloud Gateway为微服务架构提供简单、有效且统一的API路由管理方式。
Spring Cloud Gateway基于Filter链提供网关基本功能:安全、监控/埋点、限流等。
Spring Cloud Gateway是替代Netflix Zuul的一套解决方案。
Spring Cloud Gateway 核心概念
Spring Cloud Gateway设计以降低管理成本和安全风险,包含协议适配、协议转发、安全策略(WAF)、防刷、流量、监控日志等功能
。
下面介绍Spring Cloud Gateway中重要的概念:
- 路由(route)
路由信息的组成:
由一个ID、一个目的URL、一组断言工厂、一组Filter组成
。
如果路由断言为真,说明请求URL和配置路由匹配
。 - 断言(Predicate)
Java 8中的断言函数
。
Spring Cloud Gateway中的断言函数输入类型是Spring 5.0框架中的ServerWebExchange。Spring Cloud Gateway的断言函数允许开发者去定义匹配来自于Http Request中的任何信息比如请求头和参数
。 - 过滤器(Filter)
一个标准的Spring WebFilter
。
Spring Cloud Gateway中的Filter分为两种类型的Filter,分别是Gateway Filter
和Global Filter
。过滤器Filter将会对请求和响应进行修改处理。
Spring Cloud Gateway 工作原理
如上图所示,有几个知识点:
Gateway的客户端会向Spring Cloud Gateway发送请求,请求
首先
被HttpWebHandlerAdapter进行提取组装成网关上下文.
然后
网关的上下文会传递到DispatcherHandler。 DispatcherHandler是所有请求的分发处理器.
- DispatcherHandler
DispatcherHandler主要负责
分发请求对应的处理器,比如将请求分发到对应RoutePredicate-HandlerMapping(路由断言处理映射器)
。 - RoutePredicate-HandlerMapping(路由断言处理映射器)
路由断言处理映射器主要用于路由的查找,以及找到路由返回对应的FilteringWebHandler
。 - FilteringWebHandler
FilteringWebHandler主要负责组装Filter链表并调用Filter执行一系列Filter处理,然后把请求转到后端对应的代理服务处理,处理完毕之后将Response返回到Gateway客户端
。 - Filter类型
Spring Cloud Gateway和Zuul类似,有pre
和post
两种方式的filter。客户端的请求先经过“pre”类型的filter,然后将请求转发到具体的业务服务,比如一个service,收到业务服务的响应之后,再经过“post”类型的filter处理,最后返回响应到客户端。类似在zuul中指定入口filter和出口filter
。
注:在配置路由的时候,如果不指定端口的话,http默认设置端口为80,https默认设置端口为443。Spring Cloud Gateway的启动容器目前只支持Netty。
Spring Cloud Gateway 案例
网关最重要的功能是协议适配
和协议转发
,协议转发即基本的路由信息转发,本节演示最简单的Spring Cloud Gateway协议转发的实现。
使用Application.java
与yml
两种方式分别展示基础路由
Application.java
创建项目cloud-gty-basic-application
- pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
</dependencies>
- yml
server:
port: 8080
spring:
application:
name: spring-cloud-gateway
# Spring Cloud Gateway 日志配置
logging:
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
*Application.java
@SpringBootApplication
public class CloudGatewayBasic1Application {
/**
* 通过JAVA流式API自定义RouteLocatorBuilder方式定义Spring Cloud Gateway路由
* @param builder
* @return
*/
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// basic proxy
.route(r -> r.path("/jd")
.uri("http://jd.com:80/")
.id("jd_route")
).build();
}
public static void main(String[] args) {
SpringApplication.run(CloudGatewayBasic1Application.class, args);
}
}
服务启动,访问 http://localhost:8080/jd 会自动跳转到https://www.jd.com/
yml
基本与上方代码一致,Application和yml有所变化:
- Application(只是一个启动程序,没有特殊东西)
@SpringBootApplication
public class CloudGatewayBasic2Application {
public static void main(String[] args) {
SpringApplication.run(CloudGatewayBasic2Application.class, args);
}
}
yml
server:
port: 8080
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
#当访问http://localhost:8080/baidu,直接转发到https://www.baidu.com/
routes:
- id: baidu_route
uri: http://baidu.com:80/
predicates:
- Path=/baidu
# Spring Cloud Gateway 日志配置
logging:
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
yml中体现了Spring Cloud Gateway的配置
总结
至此, 两种简单的路由方式已经演示完毕,实际中我选择用yml的方式。
代码:
项目 | 端口 | 描述 | GIthub地址 |
---|---|---|---|
cloud-gty-basic-application | 8080 | 通过启动程序Java代码实现网关路由 | https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-basic-application |
cloud-gty-basic-yml | 8080 | 通过yml配置文件实现网关路由 | https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-basic-yml |
开启端点
Spring Cloud Gateway提供gateway actuator,该EndPiont提供关于Filter及routes的信息查询及指定route信息更新的Rest API接口。配置后即可查看路由情况
需在yml中添加如下配置(我修改了cloud-gty-basic-yml
的yml达到演示效果):
......
uri: http://baidu.com:80/
predicates:
- Path=/baidu
management:
endpoints:
web:
exposure:
include: '*'
enabled: false
# Spring Cloud Gateway 日志配置
logging:
......
新增management
这段代码,然后访问地址:
http://localhost:8080/actuator/gateway/routes 查看到路由情况
Spring Cloud Gateway 路由断言
Spring Cloud Gateway的路由匹配的功能是以Spring WebFlux中的Handler Mapping为基础实现的
。
Spring Cloud Gateway也是由许多的路由断言工厂组成的
。
Http Request
请求进入Spring Cloud Gateway
的时候,网关路由断言工厂对请求进行断言匹配。匹配成功允许进入,失败则返回错误信息。
断言定义
- 维基百科:
在程序设计中,断言(assertion)是一种放在程序中的一阶逻辑
(如一个结果为真或是假的逻辑判断式),目的是为了标示与验证程序开发者预期的结果
-当程序运行到断言的位置时,对应的断言应该为真
。若断言不为真时,程序会中止运行,并给出错误消息
。
After 路由断言工厂
After Route Predicate Factory 中获取一个UTC时间格式的参数
, 当请求的当前时间在配置的UTC时间之后,则会成功匹配,否则不能成功匹配
。我们通过例子说明情况:
创建项目cloud-gty-after-route
- pom省略
- Application
@SpringBootApplication
public class CloudGatewayAfterRouteApplication {
public static void main(String[] args) {
SpringApplication.run(CloudGatewayAfterRouteApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
ZonedDateTime minusTime = LocalDateTime.now().minusHours(1).atZone((ZoneId.systemDefault()));
System.out.println("minusTime : " + minusTime);
return builder.routes().route("after_route", r -> r.after(minusTime).uri("http://baidu.com")).build();
}
}
- yml
server:
port: 8081
logging:
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
#开启actuator管理api,后面要关闭
management:
endpoints:
web: # http://localhost:8080/actuator/gateway/routes
exposure:
include: "*"
## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: after_route
# uri: http://baidu.com
# predicates:
# - After=2019-01-22T17:27:58.909+08:00[Asia/Shanghai]
After时间可以由下面代码生成:
public static String getMinTime() {
String minTime = ZonedDateTime.now().minusHours(1).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
return minTime;
}
访问 http://localhost:8081/actuator/gateway/routes 显示如下:
访问 http://localhost:8081 直接跳转到 http://www.baidu.com,因为请求的当前时间在配置的UTC时间之后,否则不能成功转发!
Before 路由断言工厂
Before Route Predicate Factory 中获取一个UTC时间格式的参数
, 当请求的当前时间在配置的UTC时间之前,则会成功匹配,否则不能成功匹配
。
与After Route Predicate Factory 正好相反,例子只展出核心代码,并且不包含效果:
- Application.java
@SpringBootApplication
public class CloudGatewayBeforeRouteApplication {
public static void main(String[] args) {
SpringApplication.run(CloudGatewayBeforeRouteApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
ZonedDateTime dateTime = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
return builder.routes()
.route("before_route", r -> r.before(dateTime)
.uri("http://baidu.com"))
.build();
}
}
- yml 版
server:
port: 8081
logging:
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
#开启actuator管理api,后面要关闭
management:
endpoints:
web: # http://localhost:8080/actuator/gateway/routes
exposure:
include: "*"
## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: before_route
# uri: http://baidu.com
# predicates:
# - After=2022-01-22T17:27:58.909+08:00[Asia/Shanghai]
After的时间一定要在请求时间这之后!
Between 路由断言工厂
Between Route Predicate Factory 中获取一个UTC时间格式的参数
, 当请求的当前时间在配置的UTC时间之间,则会成功匹配,否则不能成功匹配
。
After Before相近,有2个UTC时间,例子只展出核心代码,并且不包含效果:
- Application.java
@SpringBootApplication
public class CloudGatewayBetweenRouteApplication {
public static void main(String[] args) {
SpringApplication.run(CloudGatewayBetweenRouteApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
ZonedDateTime dateTime1 = LocalDateTime.now().minusDays(1).atZone(ZoneId.systemDefault());
ZonedDateTime dateTime2 = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
return builder.routes()
.route("before_route", r -> r.between(dateTime1, dateTime2)
.uri("http://baidu.com"))
.build();
}
}
- yml
server:
port: 8081
logging:
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
#开启actuator管理api,后面要关闭
management:
endpoints:
web: # http://localhost:8080/actuator/gateway/routes
exposure:
include: "*"
## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: between_route
# uri: http://baidu.com
# predicates:
# - name: Between
# args:
# datetime1: 2022-01-22T17:27:58.907+08:00[Asia/Shanghai]
# datetime2: 2022-01-22T17:27:58.909+08:00[Asia/Shanghai]
如果在这个区间,就可以正常访问
Cookie 路由断言工厂
Cookie Route Predicate Factory 会取两个参数(Header中以“Cookie”命名的名称,对应的Key和Value)
。当请求携带的cookie和Cookie断言工厂配置的一致,则路由匹配成功,否则匹配失败
。
以下用实例表明
通过2个工程来说明Cookie路由断言工厂
工程 | 说明 |
---|---|
cloud-gty-cookie-route | Cookie断言工厂程序,这里进行判断Cookie的K/V,然后路由到对应的服务 |
cloud-gty-route-server | 被路由到的服务 |
cloud-gty-cookie-route
- Application
@SpringBootApplication
public class CloudGatewayCookieRoute {
public static void main(String[] args) {
SpringApplication.run(CloudGatewayCookieRoute.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//判断Cookie K(chocolate)对应的V是不是(ch.p),是的话路由到http://localhost:8071/test/cookie地址
return builder.routes()
.route("cookie_route", r -> r.cookie("chocolate", "ch.p")
.uri("http://localhost:8071/test/cookie"))
.build();
}
}
- yml
server:
port: 8081
spring:
application:
name: spring-cloud-gateway
logging:
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
#开启actuator管理api,后面要关闭
management:
endpoints:
web: # http://localhost:8080/actuator/gateway/routes
exposure:
include: "*"
## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: cookie_route
# uri: http://localhost:8071/test/cookie
# predicates:
# - Cookie=chocolate, ch.p
cloud-gty-route-server
Application与yml为常规启动程序,不粘贴上来
- Controller
/**
* 测试Cookies路由断言工厂
* @param request
* @param response
* @return
*/
@GetMapping("/test/cookie")
public String testGateway(HttpServletRequest request, HttpServletResponse response){
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName()+":"+cookie.getValue());
}
}
return "Spring Cloud Gateway,Hello world!";
}
分别顺序启动cloud-gty-cookie-route
和cloud-gty-route-server
- 打开接口测试工具(我用的PAW),在Header中录入Cookie和对应的值,并访问 http://localhost:8081,效果如下:
注:当Cookie设置的值是我们对应配置的,才会路由匹配成功,否则路由匹配失败
Header 路由断言工厂
Header Route Predicate Factory 根据配置的路由Header信息进行断言匹配路由,匹配成功进行转发,否则不进行转发
。
以下用实例表明
通过2个工程来说明Cookie路由断言工厂
工程 | 说明 |
---|---|
cloud-gty-header-route | Header断言工厂程序 |
cloud-gty-route-server | 被路由到的服务 |
cloud-gty-cookie-route
- Application
@SpringBootApplication
public class CloudGatewayHeaderRouteApplication {
public static void main(String[] args) {
SpringApplication.run(CloudGatewayHeaderRouteApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//判断header 对应的K/V
return builder.routes()
.route("header_route", r -> r.cookie("X-Request-Id", "frank")
.uri("http://localhost:8071/test/head"))
.build();
}
}
- yml
server:
port: 8081
spring:
application:
name: spring-cloud-gateway
logging:
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
#开启actuator管理api,后面要关闭
management:
endpoints:
web: # http://localhost:8080/actuator/gateway/routes
exposure:
include: "*"
## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: header_route
# uri: http://localhost:8071/test/cookie
# predicates:
# - Header=X-Request-Id, frank
cloud-gty-route-server
Application与yml为常规启动程序,不粘贴上来
- Controller
/**
* 测试Head路由断言工厂
* @param request
* @param response
* @return
*/
@GetMapping("/test/head")
public String testGatewayHead(HttpServletRequest request, HttpServletResponse response){
String head=request.getHeader("X-Request-Id");
return "return head info:"+head;
}
分别顺序启动cloud-gty-header-route
和cloud-gty-route-server
Host 路由断言工厂
Host Route Predicate Factory 根据配置的Host,对请求中的Host进行断言处理,断言匹配成功进行转发,否则不进行转发
。
核心代码展示:
- Application
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//判断header 对应的K/V
return builder.routes()
.route("host_route", r -> r.host("**.baidu.com:8080")
.uri("http://jd.com"))
.build();
}
- yml
## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: host_route
# uri: http://jd.com
# predicates:
# - Host=**.baidu.com:8080
- 说明
配置主机名的时候,如果Gateway端口为80则把80端口省略,如果Gateway有端口,如上所示需要配置Host=**.baidu.com:8080,需要加上端口
。 - 运行
- 通过
端口配置工具 SwitchHosts
配置本地二级域名vip.baidu.com来解析本地ip 127.0.0.1:
- 启动cloud-gty-host-route,并访问地址 http://vip.baidu.com:8081 ,将会自动转发到https://www.jd.com。
- 通过
Method 路由断言工厂
Method Route Predicate Factory 根据路由信息配置的Method对请求方式是Get或者Post等进行断言匹配,断言匹配成功进行转发,否则不进行转发
。
核心代码展示:
- Application
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//判断header 对应的K/V
return builder.routes()
.route("method_route", r -> r.method("GET")
.uri("http://jd.com"))
.build();
}
- yml
## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: method_route
# uri: http://jd.com
# predicates:
# - Method=GET
- 运行
请求http://localhost:8081,由于是GET请求,会自动断言路由到https://www.jd.com。
Query 路由断言工厂
Query Route Predicate Factory 根据请求中的两个参数进行断言匹配,断言匹配成功进行转发,否则不进行转发
。
例如地址:http://localhost:8081?flag=main,此地址后面的参数flag=main,在应用程序中配置对应的匹配,匹配则通过,不匹配则不通过,如下代码:
核心代码展示:
- Application
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//判断header 对应的K/V
return builder.routes()
.route("query_route", r -> r.query("flag", "main")
.uri("http://baidu.com"))
.build();
}
- yml
## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: query_route
# uri: http://jd.com
# predicates:
# - Query=flag, main
- 运行
请求http://localhost:8081?flag=main,根据query请求断言配置,匹配参数,会自动断言路由到https://www.baidu.com。
RemoteAddr 路由断言工厂
RemoteAddr Route Predicate Factory 配置一个IPv4或者IPv6网段的字符串或者IP。当请求IP地址在网段之内或者和配置的IP相同,断言匹配成功进行转发,否则不进行转发
。
如下所示都是可以通过的:
IP | 说明 |
---|---|
192.168.0.1 | IP地址 |
192.168.0.16 | 子网掩码,属同一个网段即有效 |
核心代码展示:
- Application
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//判断网段,在127.0.0.1内的都可以断言路由成功
return builder.routes()
.route("remoteaddr_route", r -> r.remoteAddr("127.0.0.1")
.uri("http://baidu.com"))
.build();
}
- yml
## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: remoteaddr_route
# uri: http://baidu.com
# predicates:
# - RemoteAddr=127.0.0.1
- 运行
请求http://localhost:8081,根据remoteaddr请求断言配置,是127.0.0.1网段下,匹配成功,会自动断言路由到https://www.baidu.com。
总结
路由断言项目列表
Spring Cloud Gateway 内置Filter
就是过滤器,修改请求报文和响应报文,可以用在鉴权之类的功能上
AddRequestHeader 过滤器工厂
AddRequestHeader过滤器工厂用于对匹配上对请求加上header
AddRequestParameters 过滤器
AddRequestParameters过滤器对匹配上的请求路由添加参数
RewritePath 过滤器
RewritePath过滤器去掉前缀
AddResponseHeader 过滤器
AddResponseHeader过滤器工厂对从网关返回的响应添加Header
StripPrefix 过滤器
StripPrefix过滤器去掉前缀
Retry 过滤器
Retry过滤器,设置重试机制
Hystrix 过滤器
Hystrix过滤器,熔断器