Gateway官方文档
在Spring Cloud 1.x
中,网关使用的是Zuul 1
,在Spring Cloud 2.x
中,网关使用的是Gateway
,因为Zuul 2
版本进展缓慢,所以Spring Cloud
自己研发了网关,Gateway
是原Zuul 1
的替代版。Gateway采用异步非阻塞模型开发,性能上不需要担心
,虽然Netflix发布了Zuul 2版本,但是Spring Cloud并没有整合的计划,所以才自己推出了Gateway的方案。
Spring Cloud Gateway
是Spring Cloud
的一个全新项目,基于Spring 5+Spring Boot 2.x+Project Reactor等技术开发的网关,旨在为微服务架构提供一种简单有效的统一API路由管理方式
。
Spring Cloud Gateway
作为Spring Cloud
生态系统中的网关,目标是替代Zuul 1
,在Spring Cloud 2.x
版本中,没有对新版本Zuul 2
最新高性能版本进行集成,仍然使用的Zuul 1非Reactor模式的老版本
,为了提升网关性能,Spring Cloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层使用了高性能的Reactor模式通信框架Netty
。
Spring Cloud Gateway
目标是提供统一的路由方式
,并且基于Filter
链方式提供网关基本功能:安全
、监控
、指标
、限流
。
Spring Cloud Gateway
网关的作用:反向代理
、鉴权
、流量控制
、熔断
、日志监控
等。
在微服务架构中,网关的位置,位于各个微服务的上一层,通过网关后,就访问到了具体的微服务了
。
Spring Cloud Gateway
特性:
Spring Framework 5
+ Project Reactor
+ Spring Boot 2.x
进行构建Predicate(断言
)和Filter(过滤器)
,而且编写容易Hystrix
断路器功能Spring Cloud服务发现
功能Spring Cloud Gateway
和Zuul
的区别:
在Spring Cloud Finchley
正式版之前,Spring Cloud
推荐的网关是Netflix
提供的Zuul
;
Zuul 1
采用的是阻塞I/O
的API网关Zuul 1
使用Servlet2.5的阻塞架构实现
,不支持任何长连接(如WebSocket)
,Zuul
的设计模式和Nginx比较像
,每次I/O
都从工作线程中选择一个执行,请求线程在工作线程完成之前一直是阻塞的,Nginx是C++实现
,Zuul是Java实现
,JVM
在第一次的加载时候,会比较慢,所以Zuul的性能比较差
Zuul 2
设计理念跟先进,基于Netty非阻塞和支持长连接
,但是目前Spring Cloud没有整合它
,Zuul 2
的性能比Zuul 1
性能有较大提升,Spring Cloud Gateway的性能也不错
,官方测试数据表示,Spring Cloud Gateway的RPS(每秒请求数)是Zuul 1的1.6倍
Spring Cloud Gateway
建立在Spring Framework 5 + Project Reactor + Spring Boot 2.x
之上,使用非阻塞API
Spring Cloud Gateway还支持WebSocket
,与Spring
紧密集成拥有更好的开发体验Zuul 1
模型缺点:Zuul 1采用传统Servlet I/O处理模型
,但请求进入servle
t容器时,servlet
容器会为其绑定一个线程,在并发不高的情况下是适用的,当并发量增加,线程数就会增加,但是线程资源是非常昂贵的(线程上下文切换内存消耗大),会影响到请求处理时间。有些简单的业务场景,并不需要每个请求分配一个线程,简单业务的高并发下,实际可能只需要几个线程就能扛得住,因此,每个请求分配一个线程,在高并发环境下并没有优势。
Gateway
模型:Gateway模型采用的是WebFlux框架
,这个框架是一个典型的非阻塞异步的框架
,并且在Servlet 3.1后,支持了异步非阻塞
,框架的核心是基于Reactor相关API实现的
。相对于传统Web框架,它可以运行在支持Servlet 3.1容器上的组件中(如Netty
、Undertow
等)。Spring WebFlux
是Spring 5
引入的新的响应式框架,区别于Spring MVC
,不需要依赖Servlet API
,完全异步非阻塞,并且基于Reactor来实现响应式流规范。
Spring WebFlux参考官网
路由是构建网关的基本模块,由ID
,目标URI
一系列断言和过滤器组成,如果断言为true
,则匹配该路由。
参考Java8
的java.util.function.Predicate
,开发人员可以匹配HTTP
请求中的所有内容(比如请求头,请求参数等),如果请求与断言匹配,则进行路由
。
类似于Web
开发中的过滤器,这里指的是Spring
框架中GatewayFilter实例
,使用过滤器可以在请求被路由之前或之后对请求进行修改
。
总结:
路由
的功能是由断言和过滤组合来实现的
,一个Web
请求发送后,先经过网关,网关里的断言和过滤用来判断这个请求是否需要路由转发,当断言为true,过滤器放行时候,这个请求进行路由转发,此时,请求才到达具体的微服务模块。
客户端向Spring Cloud Gateway
发送请求,然后在Gateway Handler Mapping
中找到与请求相匹配的路由,将其Web Handler
。Handler
通过指定的过滤器链将请求发送到实际服务,执行业务逻辑,然后返回。如果有post
类型的过滤器,执行过滤器逻辑。
pre类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等功能
。
post类型的过滤器可以做响应内容、响应头的修改、日志输出、流量监控等功能
。
创建cloud-gateway-gateway9527
模块,修改pom.xml
,加入spring-cloud-starter-gateway
和spring-cloud-starter-netflix-eureka-client
的依赖。
cloud-gateway-gateway9527
模块的pom.xml
文件:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020artifactId>
<groupId>com.king.springcloudgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud-gateway-gateway9527artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
使用spring-cloud-starter-gateway
依赖时不能添加spring-boot-starter-web
跟spring-boot-starter-actuator
依赖,不然会报错:
cloud-gateway-gateway9527
模块的application.yml
文件:
# 配置服务端口号
server:
port: 9527
# 配置应用信息
spring:
application:
name: cloud-gateway # 配置应用名称
# 配置eureka
eureka:
instance:
hostname: cloud-gateway-service # 实例名称
client:
register-with-eureka: true # true:将自己注册进Eureka
fetch-registry: true # true:需要去注册中心获取其他服务地址
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ # 入驻的服务地址
cloud-gateway-gateway9527
模块的主启动类:
package com.king.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* 微服务网关模块主启动类
* @EnableEurekaClient:表示是Eureka服务注册中心客户端
*/
@EnableEurekaClient
@SpringBootApplication
public class GatewayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class, args);
}
}
cloud-gateway-gateway9527
模块通过yml
给Gateway
添加路由规则。
# 配置服务端口号
server:
port: 9527
# 配置应用信息
spring:
application:
name: cloud-gateway # 配置应用名称
cloud:
# 配置spring gateway网关
gateway:
# 配置路由
routes:
# - 是yml语法,代表数组的意思
- id: payment_route1 # 路由(名字)ID,没有固定规则,需要保证唯一
uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
predicates:
- Path=/payment/get/** # 路径匹配断言,**表示RESTFul请求的参数
- id: payment_route2 # 路由(名字)ID,没有固定规则,需要保证唯一
uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
predicates:
- Path=/payment/loadBalance # 路径匹配断言
# 配置eureka
eureka:
instance:
hostname: cloud-gateway-service # 实例名称
client:
register-with-eureka: true # true:将自己注册进Eureka
fetch-registry: true # true:需要去注册中心获取其他服务地址
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ # 入驻的服务地址
相比之前的yml
,这里的yml
多了Gateway
结点,配置这个的目的在于隐藏cloud-provider-payment8001
模块的localhost:8001
地址,如果想访问payment8001
服务的/payment/get/**
地址,之前需要访问http://localhost:8001/payment/get/1
,配置了路由之后,访问http://localhost:9527/payment/get/1
也可以实现,于是,原来的8001端口就隐藏
了。
配置路由有两种方式,一种是上面的yml
配置文件,另一种是是编码方式配置,uri
中记得带http
,否则访问不到,这里的uri
可以写任意地址,代表一个路由规则,访问path
路径的时候,被路由到uri
地址。
cloud-gateway-gateway9527
模块添加配置Gateway
网关配置类:
package com.king.springcloud.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 编码方式配置Gateway网关
*/
@Configuration
public class GatewayConfig {
/**
* 配置路由
* @param routeLocatorBuilder
* @return
*/
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder) {
return routeLocatorBuilder.routes()
// 当访问http://localhost:9527/payment/get/**的时候,请求会被路由到http://localhost:8001/payment/get/**
.route("payment_route1", r -> r.path("/payment/get/**").uri("http://localhost:8001/payment/get/**"))
// 当访问http://localhost:9527/payment/loadbalance的时候,请求会被路由到http://localhost:8001/payment/loadbalance
.route("payment_route2", r -> r.path("/payment/loadbalance").uri("http://localhost:8001/payment/loadbalance"))
.build();
}
}
现在存在的问题:真实服务地址写死了,我们应该通过服务名来找服务,而不是通过服务地址找服务。
默认情况下,Gateway
会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由转发,从而实现动态路由的功能
。
为了演示网关的负载均衡,修改Provider8001
和Provider8002
的yml
配置文件,将它们的注册地址改成eureka7001
的地址。启动Eureka7001
和Provider8001
和Provider8002
服务以及Gateway9527
服务。修改Gateway9527
模块的application.yml
配置文件,修改为如下内容。因为要修改application.yml
,所以先把GatewayConfig
类进行屏蔽,避免产生影响。
cloud-gateway-gateway9527
模块通过yml
给Gateway
添加动态路由规则。
# 配置服务端口号
server:
port: 9527
# 配置应用信息
spring:
application:
name: cloud-gateway # 配置应用名称
cloud:
# 配置spring gateway网关
gateway:
# 配置动态路由
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
# 配置路由
routes:
# - 是yml语法,代表数组的意思
- id: payment_route1 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
predicates:
- Path=/payment/get/** # 路径匹配断言,**表示RESTFul请求的参数
- id: payment_route2 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
predicates:
- Path=/payment/loadBalance # 路径匹配断言
# 配置eureka
eureka:
instance:
hostname: cloud-gateway-service # 实例名称
client:
register-with-eureka: true # true:将自己注册进Eureka
fetch-registry: true # true:需要去注册中心获取其他服务地址
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ # 入驻的服务地址
通过浏览器访问http://localhost:9527/payment/loadBalance
,在页面端可以看到端口号的不断变化,如果要实现负载均衡,注意uri
中的lb
是固定的,它是识别开启负载均衡的标志。
在Gateway9527
启动的时候,在Console
可以看到如下内容,代表执行加载Predicate
,我们目前使用的Predicate
是Path
。
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [After]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Before]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Between]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Cookie]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Header]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Host]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Method]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Path]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Query]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [ReadBodyPredicateFactory]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [RemoteAddr]
2021-10-05 20:38:06.989 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Weight]
2021-10-05 20:38:06.990 INFO 10157 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [CloudFoundryRouteService]
官方文档:https://cloud.spring.io/spring-cloud-gateway/2.2.x/reference/html/#gateway-request-predicates-factories
Spring Cloud Gateway
将路由匹配作为Spring WebFlux HandlerMapping
基础架构的一部分。
Spring Cloud Gateway
包括许多内置的Route Predicate Factory
,这些Predicate HTTP
请求的不同属性匹配,多个Route Predicate Factory
可以进行组合。
Spring Cloud Gateway
创建Route路由
对象时,使用Route Predicate Factory
创建Predicate
对象,Predicate
对象可以赋值给Route
。
根据官方文档里的说明,在application.yml
里配置上即可生效,这相当于加了一个条件,条件满足就做路由转发,不满足就不路由转发。
简单说下吧,After Route Predicate
、Before Route Predicate
、Between Route Predicate
需要用到一个DateTime
值,这个值要使用ZonedDateTime
类来获取。
cloud-gateway-gateway9527
模块在测试包创建一个Test
测试类:
import java.time.ZonedDateTime;
public class Test {
public static void main(String[] args) {
// 获取默认时区时间
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime); // 2021-10-05T21:57:59.550+08:00[Asia/Shanghai]
}
}
cloud-gateway-gateway9527
模块使用After Route Predicate
、Before Route Predicate
、Between Route Predicate
修改application.yml
文件:
# 配置服务端口号
server:
port: 9527
# 配置应用信息
spring:
application:
name: cloud-gateway # 配置应用名称
cloud:
# 配置spring gateway网关
gateway:
# 配置动态路由
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
# 配置路由
routes:
# - 是yml语法,代表数组的意思
- id: payment_route1 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
predicates:
- Path=/payment/get/** # 路径匹配断言,**表示RESTFul请求的参数
- id: payment_route2 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
predicates:
- Path=/payment/loadBalance # 路径匹配断言
# - Before=2021-10-05T21:57:59.550+08:00[Asia/Shanghai] # 在这个时间之前访问才有效
# - After=2021-10-05T22:57:59.550+08:00[Asia/Shanghai] # 在这个时间之后访问才有效
- Between=2021-10-05T21:57:59.550+08:00[Asia/Shanghai],2021-10-05T22:57:59.550+08:00[Asia/Shanghai] # 在这个区间时间才有效
# 配置eureka
eureka:
instance:
hostname: cloud-gateway-service # 实例名称
client:
register-with-eureka: true # true:将自己注册进Eureka
fetch-registry: true # true:需要去注册中心获取其他服务地址
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ # 入驻的服务地址
cloud-gateway-gateway9527
模块使用Cookie Route Predicate 需要两个参数:一个是Cookie name,另外一个是正则表达式。
、Header Route Predicate 需要两个参数:一个属性名称,另外一个是正则表达式。
、Host Route Predicate
的测试使用终端
来测试,修改application.yml
文件,通过发送curl
命令即可完成,具体可以看一下curl
命令的参数介绍,打开终端:
# 配置服务端口号
server:
port: 9527
# 配置应用信息
spring:
application:
name: cloud-gateway # 配置应用名称
cloud:
# 配置spring gateway网关
gateway:
# 配置动态路由
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
# 配置路由
routes:
# - 是yml语法,代表数组的意思
- id: payment_route1 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
predicates:
- Path=/payment/get/** # 路径匹配断言,**表示RESTFul请求的参数
- id: payment_route2 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
# 配置断言
predicates:
- Path=/payment/loadBalance # 路径匹配断言
# - Before=2021-10-05T21:57:59.550+08:00[Asia/Shanghai] # 在这个时间之前访问才有效
# - After=2021-10-05T22:57:59.550+08:00[Asia/Shanghai] # 在这个时间之后访问才有效
# - Between=2021-10-05T21:57:59.550+08:00[Asia/Shanghai],2021-10-05T22:16:59.550+08:00[Asia/Shanghai] # 在这个区间时间才有效
# - Cookie=username,zhangsan # 请求中Cookie要有"username"键跟"zhangsan"值的数据才能有效访问
# - Header=X-Request-id, \d+ # 请求头要有X-Request-id属性并且值为整数的正则表达式才能有效访问
- Host=**.king.com # 请求中要加满足条件的Host才能有效访问
# 配置eureka
eureka:
instance:
hostname: cloud-gateway-service # 实例名称
client:
register-with-eureka: true # true:将自己注册进Eureka
fetch-registry: true # true:需要去注册中心获取其他服务地址
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ # 入驻的服务地址
#测试Cookie Route Predicate
test-MBP:~ test$ curl http://localhost:9527/payment/loadBalance --cookie "username=zhangsan"
8002
#测试Header Route Predicate
curl http://localhost:9527/payment/loadBalance -H "X-Request-Id:123"
8001
#测试Header Route Predicate
curl http://localhost:9527/payment/loadBalance -H "Host: www.king.com"
#测试Query Route Predicate
curl http://localhost:9527/payment/loadBalance -H "Host:
cloud-gateway-gateway9527
模块使用Method Route Predicate
是根据GET
或POST
做断言,Path Route Predicate
就是我们最初使用的方式,Query Route Predicate
是根据请求里所带Query
参数进行判断的,RemoteAddr Route Predicate
是对请求发起ip做的校验,Weight Route Predicate
是给负载均衡分配权重的,可以指定请求发到某一个uri
的权重。
# 配置服务端口号
server:
port: 9527
# 配置应用信息
spring:
application:
name: cloud-gateway # 配置应用名称
cloud:
# 配置spring gateway网关
gateway:
# 配置动态路由
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
# 配置路由
routes:
# - 是yml语法,代表数组的意思
- id: payment_route1 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
predicates:
- Path=/payment/get/** # 路径匹配断言,**表示RESTFul请求的参数
- id: payment_route2 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
# 配置断言
predicates:
- Path=/payment/loadBalance # 路径匹配断言
- Method=GET,POST # 请求方式是GET或POST才能有效访问
- Query=username, \d+ # 要有参数名username并且值一定要整数才能有效访问
# 配置eureka
eureka:
instance:
hostname: cloud-gateway-service # 实例名称
client:
register-with-eureka: true # true:将自己注册进Eureka
fetch-registry: true # true:需要去注册中心获取其他服务地址
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ # 入驻的服务地址
#测试Path Route Predicate
curl http://localhost:9527/payment/loadBalance
#测试Method Route Predicate
curl http://localhost:9527/payment/loadBalance
#测试Query Route Predicate
curl http://localhost:9527/payment/loadBalance?username=31
所以说,Predicate
可以提供一组匹配规则,每当一个请求过来的时候,去检验这些规则,如果满足规则,进行路由,否则,就提示出错。
路由过滤器可用于修改进入的HTTP
请求和返回的HTTP
响应,路由过滤器只能指定路由进行使用,Spring Cloud Gateway
内置了多种路由过滤器,它们是通过Gateway Filter Factory
产生的。
从生命周期来分类
,可以分为两种Pre 业务逻辑之前
和Post 业务逻辑之后
,类似于Spring AOP
的前置通知和后置通知。从种类上来分类
,可以分为两种Gateway Filter 单一的
和Global Filter 全局的
。具体可以参考官网,这个的配置也是在application.yml里加配置即可。
Gateway Filter
:官网使用参考文档
下面就是写一个简单的Gateway Filter
配置,其他的就可以参照上面的官方文档进行配置:
# 配置服务端口号
server:
port: 9527
# 配置应用信息
spring:
application:
name: cloud-gateway # 配置应用名称
cloud:
# 配置spring gateway网关
gateway:
# 配置动态路由
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
# 配置路由
routes:
# - 是yml语法,代表数组的意思
- id: payment_route1 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
predicates:
- Path=/payment/get/** # 路径匹配断言,**表示RESTFul请求的参数
- id: payment_route2 # 路由(名字)ID,没有固定规则,需要保证唯一
# uri: http://localhost:8001 # 路由到哪个地址,实际提供微服务的地址
uri: lb://cloud-provider-payment # 使用微服务应用名称查找路由地址,就是在Eureka注册中心里面的Application名称,lb是用于识别负载均衡的前缀,不能修改
# # 配置Gateway Filter
filters:
- AddRequestParameter=X-Request-id,1024 # 过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-id,值为1024才能有效访问
# 配置断言
predicates:
- Path=/payment/loadBalance # 路径匹配断言
- Method=GET,POST # 请求方式是GET或POST才能有效访问
- Query=username, \d+ # 要有参数名username并且值一定要整数才能有效访问
# 配置eureka
eureka:
instance:
hostname: cloud-gateway-service # 实例名称
client:
register-with-eureka: true # true:将自己注册进Eureka
fetch-registry: true # true:需要去注册中心获取其他服务地址
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ # 入驻的服务地址
Global Filter
:官网使用参考文档
下面来介绍Global Filter
自定义全局过滤器,我们可以根据业务需求做定制化处理。
package com.king.springcloud.filter;
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.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 首先自定义全局过滤器要实现两个接口:GlobalFilter、Ordered
*
* 能做"全局日志记录"、"统一网关鉴权"等等的业务功能。
*/
@Component
public class MyGatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取Request,获取参数
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
System.out.println(queryParams);
// 这里可以对queryParams里的值做一些校验
if ("".equals(queryParams.getFirst("username"))) {
System.out.println("非法用户");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
// 表示该请求已经处理完成
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 加载顺序,过滤器的优先级,值越小优先级越高
*/
@Override
public int getOrder() {
return 0;
}
}