Spring Cloud 体系内的网关技术主要有 Zuul 和 Spring Cloud Gateway。Zuul 是 Netflix 公司开源的产品,也是 Spring Cloud F版本 之前默认使用的服务网关组件,但是随着 Netflix 公司一系列的停更事件,在 Spring Cloud G版本后已经不建议采用 Zuul 技术,官方建议使用 Spring Cloud Gateway 作为默认的网关技术。
微服务架构中网关在哪里?
1. Spring Cloud Gateway简介
Spring Cloud Gateway 是 Spring 体系内的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,在SpringCloud 2.0以上版本中,没有对新版本的Zuul 2.0 以上最新高性能版本进行集成,仍然还是使用Zuul 1.x 非 Reactor 模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架低层则使用了高性能的Reactor模式通信框架Netty。
Spring Cloud Gateway 的目标是提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全、监控/指标和限流。
Spring Cloud Gateway 特性
- 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
- 动态路由
- Predicates 和 Filters 作用于特定路由
- 集成 Hystrix 断路器
- 集成 Spring Cloud DiscoveryClient
- 易于编写的 Predicates 和 Filters
- 限流
- 路径重写
Spring Cloud Gateway 中的相关概念
- Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为true,则路由匹配。
- Predicate(断言):参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
- Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,可以在请求被路由前或者之后对请求和响应进行修改。
Spring Cloud Gateway工作原理
客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
虚线左边的是 pre 类型过滤器,虚线右边的是 post 类型过滤器,外部请求进来后先经过 pre 类型的过滤器,再发送到代理服务,代理服务响应请求,再次经过 post 过滤器链,最后传递给前端。
Filter在 pre 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在 post 类型过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等。
Spring Cloud Gateway 与 Zuul 对比
- Zuul 是 Netflix 开源的产品,Spring Cloud Gateway 是 Spring 体系内的产品,和 Spring 体系融合更好。
- Zuul1 不支持长连接,比如 WebSockets;Spring Cloud Gateway 支持 WebSockets 等协议。
- 作为一个服务网关产品,Spring Cloud Gateway 考虑更全面一些,增加了 Predicate、限流等技术。
- Zuul1 是基于 Servlet 框架构建,采用的是阻塞和多线程方式,即一个线程处理一次连接请求,这种方式在内部延迟严重、设备故障较多情况下会引起存活的连接增多和线程增加的情况发生。
- Spring Cloud Gateway 基于 Spring 5.0、Spring Boot 2.0 和 Project Reactor 开发,采用了 Netty 实现了异步非阻塞模型,占用资源更小,在性能方面更有优势。
2. Spring Cloud Gateway实践
目前项目 006SpringCloud 已提供了几个模块,都是前面文章的demo:
- eureka注册中心: eureka-server-7001和 eureka-server-7002
- 服务提供者:provider-payment9001 和 provider-payment9002
- …
服务提供者中都提供了接口 “/payment/get/{id}”,返回的是 CommonResult<Payment> 。
@RestController
@RequestMapping("/payment")
public class PaymentController {
private Logger logger = LoggerFactory.getLogger(PaymentController.class);
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
if (id != null && id % 2 == 0) {
Payment payment = Payment.newInstance().setSerial(UUID.randomUUID().toString()).setId(id);
return CommonResult.newInstance().setCode(200).setMessage("查询成功,serverPort:" + serverPort).setData(payment);
} else {
return CommonResult.newInstance().setCode(444).setMessage("没有对应记录,查询ID: " + id).setData(null);
}
}
...
}
目前项目 006SpringCloud 新建模块 gateway-6001,并在 pom.xml 中添加 Spring Cloud Gateway 依赖。
gateway-6001的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>006SpringCloud</artifactId>
<groupId>com.xander</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway-6001</artifactId>
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
启动类Gateway6001.java
@SpringBootApplication
@EnableDiscoveryClient
public class Gateway6001 {
public static void main(String[] args) {
SpringApplication.run(Gateway6001.class, args);
}
}
application.yml
server:
port: 6001
eureka:
instance:
hostname: gateway
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
# 集群版-
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
logging:
level:
# 调整 gateway 包的 log 级别,以查看 Gateway 相关的日志
org.springframework.cloud.gateway: debug
spring:
application:
name: gateway
**需求:**通过gateway-6001网关服务实现路由转发的功能,访问 http://localhost:6001/payment/get/{id}
时会自动转发到地址:http://localhost:9001/payment/get/{id}
。
Gateway网关路由有两种配置方式:
- Spring容器中注入RouteLocator的Bean
- 在配置文件yml中配置
2.1 配置方式一:注入RouteLocator的Bean
RouteConfig.java路由配置类
@Configuration
public class RouteConfig {
/**
* 自定义 RouteLocator
* @return
*/
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//以 /payment/ 开头的uri,都转发给 http://localhost:9001
return builder.routes()
//路由id: path_route; 匹配的路径:/payment/**
.route("path_route", r -> r.path("/payment/**")
// 匹配成功转发给:http://localhost:9001
.uri("http://localhost:9001"))
.build();
}
}
启动服务后,访问 http://localhost:6001/payment/get/2
,网关转发请求成功。
查看gateway匹配路由以及转发日志,可以看到匹配到了id为path_route的路由:
2021-05-31 17:03:23.942 DEBUG 21720 --- [ctor-http-nio-5] o.s.c.g.h.RoutePredicateHandlerMapping : Route matched: path_route
2021-05-31 17:03:23.942 DEBUG 21720 --- [ctor-http-nio-5] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:6001/payment/get/2] to Route{id='path_route', uri=http://localhost:9001, order=0, predicate=Paths: [/payment/**], match trailing slash: true, gatewayFilters=[], metadata={}}
2021-05-31 17:03:23.942 DEBUG 21720 --- [ctor-http-nio-5] o.s.c.g.h.RoutePredicateHandlerMapping : [20f7ce0c-4] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@13f764f7
2.2 配置方式二:在配置文件yml中配置
注释掉RouteLocator Bean
@Configuration
public class RouteConfig {
/**
* 自定义 RouteLocator
* @return
*/
// @Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//以 /payment/ 开头的uri,都转发给 http://localhost:9001
return builder.routes()
//路由id: path_route; 匹配的路径:/payment/**
.route("path_route", r -> r.path("/payment/**")
// 匹配成功转发给:http://localhost:9001
.uri("http://localhost:9001"))
.build();
}
}
application.yml 中添加配置
spring:
cloud:
gateway:
routes:
# 路由配置
- id: payment_route
# 转发到 http://localhost:9001
uri: http://localhost:9001
predicates:
# 断言,匹配路径 /payment/**
- Path=/payment/**
重启服务后,访问 http://localhost:6001/payment/get/2
,网关转发请求成功。
查看gateway匹配路由以及转发日志,可以看到匹配到了id为 payment_route 的路由:
2021-05-31 17:18:13.433 DEBUG 16148 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping : Route matched: payment_route
2021-05-31 17:18:13.433 DEBUG 16148 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:6001/payment/get/2] to Route{id='payment_route', uri=http://localhost:9001, order=0, predicate=Paths: [/payment/**], match trailing slash: true, gatewayFilters=[], metadata={}}
2021-05-31 17:18:13.433 DEBUG 16148 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping : [524a4282-1] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@4cb1d057
3. 负载均衡
当路由配置中 uri 所用的协议为 lb
时(以 uri: lb://paymentService 为例),gateway 将使用 LoadBalancerClient 把 paymentService 通过 eureka 注册中心拉取的服务列表解析为实际的主机和端口,并进行负载均衡。
application.yml 中修改配置
spring:
cloud:
gateway:
routes:
# 路由配置
- id: payment_route
# 转发到 http://localhost:9001
# uri: http://localhost:9001
# lb协议,负载均衡,轮询转发到
uri: lb://paymentService
predicates:
# 断言,匹配路径 /payment/**
- Path=/payment/**
重启服务,访问 http://localhost:6001/payment/get/2
,负载均衡已生效
4. 根据 serviceId 转发请求
开启根据 serviceId 转发spring.cloud.gateway.discovery.locator.enabled=true
:是否开启通过注册中心进行路由转发的功能,通过 serviceId 转发到服务,默认为 false。
application.yml 中修改配置
spring:
application:
name: gateway
cloud:
gateway:
routes:
# 路由配置
# - id: payment_route
# # 转发到 http://localhost:9001
## uri: http://localhost:9001
# # lb协议,负载均衡,轮询转发到
# uri: lb://paymentService
# predicates:
# # 断言,匹配路径 /payment/**
# - Path=/payment/**
# 开启根据 serviceId 转发,负载均衡也生效
discovery:
locator:
enabled: true
重启服务,访问 http://localhost:6001/PAYMENTSERVICE/payment/get/2
,根据服务id进行转发,同时负载均衡也生效。
5. Predicate 断言
Predicate 是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。
Spring Cloud Gateway 是通过 Spring WebFlux 的 HandlerMapping 做为底层支持来匹配到转发路由,Spring Cloud Gateway 内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。
注意: 如果有多个路由中配置的 Predicate 都能够匹配到请求,则在配置文件中自上而下只将请求交给第一个匹配到的路由处理。
5.1 通过时间匹配:After、Before、Between
After Route Predicate Factory 采用一个参数——日期时间。在该日期时间之后发生的请求都将被匹配。
application.yml
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
Before Route Predicate Factory 采用一个参数——日期时间。在该日期时间之前发生的请求都将被匹配。
application.yml.
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
Between Route Predicate Factory 有两个参数,datetime1和datetime2。在datetime1和datetime2之间的请求将被匹配。datetime2参数的实际时间必须在datetime1之后。
application.yml.
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[Asia/Shanghai], 2017-01-21T17:42:47.789-07:00[Asia/Shanghai]
5.2 Cookie 匹配
Cookie Route Predicate Factory 有两个参数,cookie名称和正则表达式。
断言匹配条件:
请求包含cookie名称并且该cookie的值被正则表达式匹配为true。
application.yml
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Cookie=chocolate, ch.p
5.3 Header 匹配
Header Route Predicate Factory 有两个参数,header名称和正则表达式。
断言匹配条件:
请求包含次header名称并且该header的值被正则表达式匹配为true。
application.yml.
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://example.org
predicates:
- Header=X-Request-Id, \\d+
5.4 Host 匹配
Host Route Predicate Factory 包括一个参数:host name列表。使用Ant路径匹配规则,.
作为分隔符。
application.yml.
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
5.5 Method 请求方法匹配
Method Route Predicate Factory 只包含一个参数: 需要匹配的HTTP请求方式
application.yml.
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://example.org
predicates:
- Method=GET
所有GET请求都将被路由。
5.6 Path 请求路径匹配
Path Route Predicate Factory 有2个参数: 一个Spring PathMatcher
表达式列表和可选的matchOptionalTrailingSeparator
标识 .
application.yml.
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://example.org
predicates:
- Path=/foo/{segment},/bar/{segment}
例如: /foo/1
or /foo/bar
or /bar/baz
的请求都将被匹配
URI 模板变量 (如上例中的 segment
) 将以Map的方式保存于ServerWebExchange.getAttributes()
key为ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
. 这些值将在[GatewayFilter Factories]使用
可以使用以下方法来更方便地访问这些变量。
Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);
String segment = uriVariables.get("segment");
5.7 Query 请求参数匹配
Query Route Predicate Factory 有2个参数: 必选项参数名 param
和可选项 regexp
值匹配的正则表达式.
application.yml.
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=baz
则包含了请求参数 baz
的都将被匹配。
application.yml.
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=foo, ba.
如果请求参数里包含foo
参数,并且值匹配为ba.
正则表达式,则将会被路由,如:bar
and baz
。
6. Filter 过滤器
Spring Cloud Gateway 的只提供了两个Filter :“pre” 和 “post”。
-
PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现权限管理、安全校验、记录调试信息等。
-
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
Spring Cloud Gateway 的 Filter 分为两种:GatewayFilter 与 GlobalFilter。GlobalFilter 会应用到所有的路由上,而 GatewayFilter 将应用到单个路由或者一个分组的路由上。
Spring Cloud Gateway 内置了 24 种 GatewayFilter 和 9 种GlobalFilter。
下面挑选几个GatewayFilter讲解一下,下面案例都来自Spring Cloud Gateway 官网。
6.1 AddRequestHeader/RemoveRequestHeader 添加和删除请求头
AddRequestHeader 采用header的名称和值作为参数。
案例:对于所有匹配的请求,向请求中添加请求头 x-request-foo:bar
。
application.yml.
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar
RemoveRequestHeader 有一个name
参数. 这是要删除的header的名称。
案例:这将在请求被发送到下游之前删除X-Request-Foo
header。
application.yml.
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: http://example.org
filters:
- RemoveRequestHeader=X-Request-Foo
6.2 AddRequestParameter 添加请求参数
采用请求参数的名称和值作为参数。
案例:对于所有匹配的请求,向请求中添加foo=bar
查询参数。
application.yml.
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: http://example.org
filters:
- AddRequestParameter=foo, bar
6.3 AddResponseHeader/RemoveResponseHeader 添加和删除响应头
AddResponseHeader采用响应header的名称和值作为参数。
案例:对于所有匹配的请求,添加响应头 x-response-foo:bar
。
application.yml.
spring:
cloud:
gateway:
routes:
- id: add\_request\_header_route
uri: http://example.org
filters:
- AddResponseHeader=X-Response-Foo, Bar
RemoveResponseHeader有一个name
参数. 这是要删除的header的名称。
案例:这将在返回到网关client之前从响应中删除x-response-foo
响应头。
application.yml.
spring:
cloud:
gateway:
routes:
- id: removeresponseheader_route
uri: http://example.org
filters:
- RemoveResponseHeader=X-Response-Foo
6.4 PrefixPath 请求的路径加前缀
PrefixPath GatewayFilter 只有一个 prefix
参数.
案例:这将给所有匹配请求的路径加前缀/mypath
。因此,向/hello
发送的请求将发送到http://example.org/mypath/hello
。
application.yml.
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://example.org
filters:
- PrefixPath=/mypath
6.5 RedirectTo 重定向
该过滤器有一个 status
和一个 url
参数。status是300类重定向HTTP代码,如301。该URL应为有效的URL,这将是 Location
header的值。
案例:这将发送一个302状态码和一个Location:http://acme.org
header来执行重定向。
application.yml.
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://example.org
filters:
- RedirectTo=302, http://acme.org
6.6 RewritePath 重写请求路径
包含一个 regexp
正则表达式参数和一个 replacement
参数. 通过使用Java正则表达式灵活地重写请求路径。
案例:对于请求路径/foo/bar
,将在发出下游请求之前将路径设置为/bar
。注意,由于YAML规范,请使用 $\
替换 $
。
application.yml.
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: http://example.org
predicates:
- Path=/foo/**
filters:
- RewritePath=/foo/(?<segment>.*), /$\{segment}
6.7 SetPath GatewayFilter Factory
SetPath GatewayFilter Factory 采用 template
路径参数。它提供了一种通过允许路径的模板化 segments 来操作请求路径的简单方法。使用Spring Framework中的URI模板,允许多个匹配的segments。
案例:对于一个 /foo/bar
请求,在做下游请求前,路径将被设置为/bar
。
application.yml.
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: http://example.org
predicates:
- Path=/foo/{segment}
filters:
- SetPath=/{segment}
6.8 SetResponseHeader 设置响应头
SetResponseHeader GatewayFilter Factory 包括 name
和 value
参数,此GatewayFilter使用给定的名称替换所有名称匹配的header的值,而不是添加。
案例:如果下游服务器响应为X-Response-Foo:1234
,则会将其替换为X-Response-Foo:Bar
, 这是网关客户端将接收的内容。
application.yml.
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: http://example.org
filters:
- SetResponseHeader=X-Response-Foo, Bar
6.9 SetStatus 设置响应状态码
SetStatus GatewayFilter Factory 包括唯一的 status
参数.必须是一个可用的Spring HttpStatus
。它可以是整数值404
或字符串枚举NOT_FOUND
。
案例:HTTP返回码将设置为401.
application.yml.
spring:
cloud:
gateway:
routes:
- id: setstatusstring_route
uri: http://example.org
filters:
- SetStatus=BAD_REQUEST
- id: setstatusint_route
uri: http://example.org
filters:
- SetStatus=401
6.10 StripPrefix 去除请求路径中的节数
StripPrefix GatewayFilter Factory 包括一个parts
参数。 parts
参数指示在将请求发送到下游之前,要从请求中去除的路径中的节数。
案例:当通过网关发出/name/bar/foo
请求时,向nameservice
发出的请求将是http://nameservice/foo
。
application.yml.
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: http://nameservice
predicates:
- Path=/name/**
filters:
- StripPrefix=2
6.11 RequestSize 请求大小限制
当请求大小大于允许的限制时,RequestSize GatewayFilter Factory可以限制请求不到达下游服务。过滤器以RequestSize
作为参数,这是定义请求的大小限制(以字节为单位)。
案例:当请求因大小而被拒绝时, RequestSize GatewayFilter Factory 将响应状态设置为413 Payload Too Large
,并带有额外的header errorMessage
。
application.yml.
spring:
cloud:
gateway:
routes:
- id: request_size_route
uri: http://localhost:8080/upload
predicates:
- Path=/upload
filters:
- name: RequestSize
args:
maxSize: 5000000
下面是一个 errorMessage
的例子。errorMessage
: Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB
注意: 默认请求大小为5 MB。
7. 自定义 GatewayFilter 和 GlobalFilter
7.1 自定义 GatewayFilter
对于匹配到的请求,不管请求路径是什么,固定转发路径为 /payment/get/2 。
FixedPathGateWayFilter.java
/**
* Description: 不管请求路径是什么,固定转发路径为 /payment/get/2 的Filter
*
* @author Xander
* datetime: 2021-03-23 20:45
*/
@Component
public class FixedPathGateWayFilter extends AbstractGatewayFilterFactory {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
logger.info("请求路径:"+request.getURI().getPath());
//新建 ServerHttpRequest 实例,固定转发路径为 /payment/get/2
ServerHttpRequest newRequest = request.mutate().path("/payment/get/2").build();
//重新build exchange
exchange = exchange.mutate().request(newRequest).build();
logger.info("固定后路径:"+exchange.getRequest().getURI().getPath());
return chain.filter(exchange);
};
}
}
application.yml修改gateway配置
spring:
application:
name: gateway
cloud:
gateway:
routes:
# 路由配置
- id: payment_route
# 转发到 http://localhost:9001
# uri: http://localhost:9001
# lb协议,负载均衡,轮询转发到
uri: lb://paymentService
predicates:
# 断言,匹配路径 /payment/**
- Path=/payment/**
filters:
# 不管请求路径是什么,固定转发路径为 /payment/get/2 的Filter
# 配置自定义GateWayFilter
- name: FixedPathGateWayFilter
# 开启根据 serviceId 转发,负载均衡也生效
# discovery:
# locator:
# enabled: true
只要发送被 /payment/** 匹配的请求,都会转发到 /payment/get/2
日志打印:
2021-05-31 21:13:32.599 DEBUG 20548 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping : Route matched: payment_route
2021-05-31 21:13:32.599 DEBUG 20548 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:6001/payment/abc] to Route{id='payment_route', uri=lb://paymentService, order=0, predicate=Paths: [/payment/**], match trailing slash: true, gatewayFilters=[[com.xander.gateway.filters.FixedPathGateWayFilter$$Lambda$607/1916822932@2ee00c8e, order = 1]], metadata={}}
...
2021-05-31 21:13:32.600 INFO 20548 --- [ctor-http-nio-2] c.x.g.filters.FixedPathGateWayFilter : 请求路径:/payment/abc
2021-05-31 21:13:32.602 INFO 20548 --- [ctor-http-nio-2] c.x.g.filters.FixedPathGateWayFilter : 固定后路径:/payment/get/2
7.2 自定义 GlobalFilter
我们可以通过实现 GlobalFilter 接口来定义一个全局过滤器,并且注入一个自定义 GlobalFilter的Bean到Spring容器中,全局过滤器即可生效,作用与所有的Route。
如果同时配置了 GateWayFilter 和 GlobalFilter,默认情况下GateWayFilter作用顺序优先于GlobalFilter,我们可以通过实现 Ordered接口来修改过滤器的作用顺序。
下面例子,新建一个全局GlobalFilter,打印请求日志。
LogGlobalFilter.java
@Component
public class LogGlobalFilter implements GlobalFilter, Ordered {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
//打印请求uri
logger.info("全局过滤器LogGlobeFilter:" + request.getURI().getPath());
return chain.filter(exchange);
}
@Override
public int getOrder() {
// order: 加载顺序,数值越小,优先级越高
return 0;
}
}
访问 http://localhost:6001/payment/abc, 可以看到全局过滤器LogGlobeFilter的日志打印
2021-05-31 21:21:13.481 INFO 11032 --- [ctor-http-nio-2] c.x.gateway.filters.LogGlobalFilter : 全局过滤器LogGlobeFilter:/payment/abc
2021-05-31 21:21:13.481 INFO 11032 --- [ctor-http-nio-2] c.x.g.filters.FixedPathGateWayFilter : 请求路径:/payment/abc
2021-05-31 21:21:13.487 INFO 11032 --- [ctor-http-nio-2] c.x.g.filters.FixedPathGateWayFilter : 固定后路径:/payment/get/2
代码:
github.com/wengxingxia/006SpringCloud.git