案例说明,下面案例复用之前所学章节中的模块
服务提供者(启用两个的作用是展示GateWay实现的负载均衡)
spring-cloud-eureka-provider-book-8002
spring-cloud-eureka-provider-book-9002
GateWay模块
spring-cloud-gateway-8005
(下面新建)
注册中心
spring-cloud-eureka-server-9001
GateWay 使用
先学习下断言是如何使用的。
- 创建GateWay模块
spring-cloud-gateway-8005
,修改其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模块依赖中,下面注释依赖不要引入,不然会冲突-->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>-->
- 在
<font style="color:rgb(68, 68, 68);">/resources</font>
目录下,添并修改application.yaml
配置文件
server:
port: 8005
spring:
application:
name: spring-cloud-gateway-8005
#############################新增网关配置###########################
cloud:
gateway:
discovery:
locator:
enabled: true # 默认值为 true,即默认开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: provider_book_list_routh # 路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://127.0.0.1:9002/ # 匹配后提供服务的路由地址
predicates: # 断言,路径相匹配的进行路由
- Path=/book/list/** # 断言,路径匹配 注意:Path 中 P 为大写
- Method=GET # 只能时 GET 请求时,才能访问
####################################################################
eureka:
instance:
hostname: spring-cloud-gateway-8005
client: # 服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://127.0.0.1:9001/eureka
以上配置中,我们在 spring.cloud.gateway.routes
下使用 predicates
属性,定义了以下两个断言条件:
只有当外部(客户端)发送到
- Path=/book/list/**
- Method=GET
spring-cloud-gateway-8005
的 HTTP 请求同时满足以上所有的断言时,该请求才会被转发到指定的服务端中(即http://127.0.0.1:9002/)。
- 主启动类上添加启用Eureka注解
@EnableEurekaClient
GateWay不需要额外添加什么注解
@SpringBootApplication
@EnableEurekaClient
public class SpringCloudGateway8005Application {
public static void main(String[] args) {
SpringApplication.run(SpringCloudGateway8005Application.class, args);
}
}
- 测试,依次启动服务注册中心,服务提供者,GateWay模块
然后在GateWay服务模块中,访问 http://127.0.0.1:8005/book/list,结果如下。最终匹配到我们指定路径上 [http://127.0.0.1:9002/book/list](http://127.0.0.1:9002/book/list)
GateWay 动态路由
默认情况下,Spring Cloud Gateway 会根据服务注册中心(例如 Eureka Server)中维护的服务列表,以服务名(spring.application.name)作为路径创建动态路由进行转发,从而实现动态路由功能。我们可以在配置文件中,将 Route 的 uri 地址修改为以下形式。
以上配置说明如下:
lb://server-name
- lb:uri 的协议,表示开启 Spring Cloud Gateway 的负载均衡功能。
- service-name:服务名,Spring Cloud Gateway 会根据它获取到具体的微服务地址。
application.yaml
即可,如下
server:
port: 8005
spring:
application:
name: spring-cloud-gateway-8005
#############################新增网关配置###########################
cloud:
gateway:
discovery:
locator:
enabled: true # 默认值为 true,即默认开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: provider_book_list_routh # 路由 id,没有固定规则,但唯一,建议与服务名对应
uri: lb://spring-cloud-eureka-provider-book-9002 #动态路由,使用服务名代替上面的具体带端口
predicates: # 断言,路径相匹配的进行路由
- Path=/book/list/** # 断言,路径匹配 注意:Path 中 P 为大写
- Method=GET # 只能时 GET 请求时,才能访问
# routes:
# - id: provider_book_list_routh # 路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://127.0.0.1:9002/ # 匹配后提供服务的路由地址
# predicates: # 断言,路径相匹配的进行路由
# - Path=/book/list/** # 断言,路径匹配 注意:Path 中 P 为大写
# - Method=GET # 只能时 GET 请求时,才能访问
####################################################################
eureka:
instance:
hostname: spring-cloud-gateway-8005
client: # 服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://127.0.0.1:9001/eureka
测试,依次启动服务注册中心,服务提供者,GateWay模块,访问 http://127.0.0.1:8005/book/list 请求结果如下:
如果连续访问,会发现这里会发生负载均衡的调用,分别会掉到不同端口的服务提供者模块上
Filter 过滤器
通常情况下,出于安全方面的考虑,服务端提供的服务往往都会有一定的校验逻辑,例如用户登陆状态校验、签名校验等。 在微服务架构中,系统由多个微服务组成,所有这些服务都需要这些校验逻辑,此时我们就可以将这些校验逻辑写到 Spring Cloud Gateway 的 Filter 过滤器中。 Filter 的分类 Spring Cloud Gateway 提供了以下两种类型的过滤器,可以对请求和响应进行精细化控制。- Pre 类型:这种过滤器在请求被转发到微服务之前可以对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等操作。
- Post 类型:这种过滤器在微服务对请求做出响应后可以对响应进行拦截和再处理,例如修改响应内容或响应头、日志输出、流量监控等。
- GatewayFilter:应用在单个路由或者一组路由上的过滤器。
- GlobalFilter:应用在所有的路由上的过滤器。
GatewayFilter 网关过滤器
GatewayFilter 是 Spring Cloud Gateway 网关中提供的一种应用在单个或一组路由上的过滤器。它可以对单个路由或者一组路由上传入的请求和传出响应进行拦截,并实现一些与业务无关的功能,比如登陆状态校验、签名校验、权限校验、日志输出、流量监控等。 GatewayFilter 在配置文件(例如 application.yml)中的写法与 Predicate 类似,格式如下。
#############################新增网关配置###########################
cloud:
gateway:
discovery:
locator:
enabled: true #默认值为 true,即默认开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: provider_book_list_routh # 路由 id,没有固定规则,但唯一,建议与服务名对应
uri: lb://spring-cloud-eureka-provider-book-9002 #动态路由,使用服务名代替上面的具体带端口
predicates: # 断言,路径相匹配的进行路由
- Path=/list/** # 断言,路径匹配 注意:Path 中 P 为大写
- Method=GET # 只能时 GET 请求时,才能访问
filters:
- AddRequestParameter=X-Request-Id,1024 #过滤器工厂会在匹配的请求头加上一对请求头,名称为 X-Request-Id 值为 1024
- PrefixPath=/book # 在请求路径前面加上 /book
添加 filters
属性,我们只需要在浏览器中访问 http://127.0.0.1:8005/list,过滤器会自动加上book,组装成http://127.0.0.1:8005/book/list进行访问
路由过滤器 | 描述 | 参数 | 使用示例 |
---|---|---|---|
AddRequestHeader | 拦截传入的请求,并在请求上添加一个指定的请求头参数。 | name:需要添加的请求头参数的 key; value:需要添加的请求头参数的 value。 | - AddRequestHeader=my-request-header,1024 |
AddRequestParameter | 拦截传入的请求,并在请求上添加一个指定的请求参数。 | name:需要添加的请求参数的 key; value:需要添加的请求参数的 value。 | - AddRequestParameter=my-request-param,c.biancheng.net |
AddResponseHeader | 拦截响应,并在响应上添加一个指定的响应头参数。 | name:需要添加的响应头的 key; value:需要添加的响应头的 value。 | - AddResponseHeader=my-response-header,c.biancheng.net |
PrefixPath | 拦截传入的请求,并在请求路径增加一个指定的前缀。 | prefix:需要增加的路径前缀。 | - PrefixPath=/consumer |
PreserveHostHeader | 转发请求时,保持客户端的 Host 信息不变,然后将它传递到提供具体服务的微服务中。 | 无 | - PreserveHostHeader |
RemoveRequestHeader | 移除请求头中指定的参数。 | name:需要移除的请求头的 key。 | - RemoveRequestHeader=my-request-header |
RemoveResponseHeader | 移除响应头中指定的参数。 | name:需要移除的响应头。 | - RemoveResponseHeader=my-response-header |
RemoveRequestParameter | 移除指定的请求参数。 | name:需要移除的请求参数。 | - RemoveRequestParameter=my-request-param |
RequestSize | 配置请求体的大小,当请求体过大时,将会返回 413 Payload Too Large。 | maxSize:请求体的大小。 | - name: RequestSize args: maxSize: 5000000 |
GlobalFilter滤器
- 在
config
下创建MyGlobalFilter
全局过滤器配置类,实现如下:
/**
* 自定义全局网关过滤器(GlobalFilter)
*/
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("进入自定义的全局过滤器 MyGlobalFilter" + new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname == null) {
log.info("参数 uname 不能为 null!");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
//过滤器的顺序,0 表示第一个
return 0;
}
}
上方我们定义了所有请求都需要验证uname不能为null的过滤器,如果为null调用失败。
- 重启当前服务,浏览器访问 http://127.0.0.1:8005/book/list,结果如下
查看后台打印日志如下:
2022-10-14 11:09:14.313 INFO 27128 --- [ctor-http-nio-2] c.c.s.config.MyGlobalFilter : 进入自定义的全局过滤器 MyGlobalFilterFri Oct 14 11:09:14 CST 2022
2022-10-14 11:09:14.315 INFO 27128 --- [ctor-http-nio-2] c.c.s.config.MyGlobalFilter : 参数 uname 不能为 null!
当我们请求时加上uname,去访问 http://127.0.0.1:8005/book/list?uname=1 结果如下