官网文档: https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
一. 下一代微服务网关Gateway
来自官方文档的一句话: Spring Cloud Gateway提供了一个在Spring生态系统之上构建的API网关,包括:Spring 5,Spring Boot 2和Project Reactor。Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注,例如:安全性,监视/指标和弹性。并且从引入的spring-cloud-starter-gateway
依赖信息可见,Spring Cloud Gateway底层是基于SpringBoot2.x、Project Reactor和Spring5 WebFlux响应式驱动编程构建,而Spring5底层又是基于netty异步实现,可谓性能不一般啊。
由于响应式编程的特性,Spring WebFlux和Reactor底层需要支持异步的运行环境,比如Netty和Undertow;也可以运行在支持异步I/O的Servlet 3.1的容器之上,比如Tomcat(8.0.23及以上)和Jetty(9.0.4及以上)。而且Spring Cloud Gateway底层默认通过netty启动,所以在使用Gateway网关组件时,无需引入Tomcat容器组件;
二. 网关解决了什么问题
网关具有身份认证与安全、审查与监控、动态路由、负载均衡、缓存、请求分片与管理、静态响应处理等功能。当然最主要的职责还是与“外界联系”。
总结一下,网关应当具备以下功能:
性能: API 高可用,负载均衡,容错机制。
安全: 权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。
日志: 日志记录,一旦涉及分布式,全链路跟踪必不可少。
缓存: 数据缓存。
监控: 记录请求响应数据,API 耗时分析,性能监控。
限流: 流量控制,错峰流控,可以定义多种限流规则。
灰度: 线上灰度部署,可以减小风险。
路由: 动态路由规则。
三. Gateway与Zuul的关系
Zuul是由Netflix公司所开源的一款网关组件,SpringBoot只是将Zuul集成在了Spring Cloud中使用,完善Spring Cloud生态圈;而Spring Cloud Gateway是Spring Cloud的一个子项目,在Zuul出来之后,由Spring自己又研发出来了下一代微服务网关组件,取名为:Spring Cloud Gateway;Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且还基于 Filter 链的方式提供了网关基本的功能。目前最新版 spring-cloud-starter-netflix-zuul依赖中引用的还是 Zuul 1.x 版本,而Zuul 1.x 版本是一个基于阻塞IO的API 网关,是阻塞 IO,不支持长连接。
在Zuul1.x版本中,默认是阻塞的,但是2019 年 5 月,Netflix 开源了支持异步调用模式的 Zuul 2.0 版本,基于Netty,是非阻塞的,支持长连接的API网关。
四. Gateway核心词汇表
Route: 网关的基本构建块路由。它由ID,目标URI,谓词集合和过滤器集合定义。如果聚合谓词为true,则匹配路由。
Predicate: 这是Java 8函数谓词,Spring Cloud Gateway 中的谓词函数输入类型是 Spring 5.0 框架中的 ServerWebExchange。Spring Cloud Gateway 中的谓词函数允许开发者去定义匹配来自于 Http Request 中的任何信息,比如请求头和参数等。
Filter: 一个标准的 Spring Web Filter。Spring Cloud Gateway 中的 Filter 分为两种类型,分别是 Gateway Filter 和 Global Filter。过滤器将会对请求和响应进行处理。
五. Gateway工作原理
下图从总体上概述了Spring Cloud Gateway的工作方式:
流程: 客户端向Spring Cloud Gateway
发出请求,然后Gateway Handler Mapping寻找确定请求与路由匹配,匹配到路由后,将其发送到 Gateway Web Handler,Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回响应结果。
六. 准备工作
搭建注册中心springcloud-eureka-server:8888
、网关服务springcloud-api-gateway:8015
、商品服务springcloud-product-service:8016
三个工程。客户端发起请求,调用API接口,首先通过gateway服务,路由到具体的API服务接口执行业务逻辑,返回响应结果。
1. Eureka注册中心
eureka注册中心工程在前面的每篇博客环境中都有用到,工程已经有现有的,不需要再重复搭建,所以这里搭建注册中心环境的过程省略掉,小伙伴们可以去GitHub上下载eureka已经搭好的工程:
https://github.com/Thinkingcao/SpringCloudLearning/tree/master/springcloud-eureka
2. Gateway网关服务
pom.xml
org.example spring-api-gateway 1.0-SNAPSHOT springcloud-api-gateway SpringCloud整合Gateway网关服务 org.springframework.boot spring-boot-starter-parent 2.0.3.RELEASE <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> org.springframework.cloud spring-cloud-dependencies ${ spring-cloud.version} pom import org.springframework.cloud spring-cloud-starter-gateway org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.projectlombok lombok application.yml
server: port: 8015 #定义服务名称 spring: application: name: api-gateway #在此指定服务注册中心地址,将当前服务注册到eureka注册中心上 eureka: client: service-url: defaultZone: http://127.0.0.1:8888/eureka #启动注册操作,该值默认为true。若设置为fasle将不会启动注册操作。是否需要去检索寻找服务,默认是true register-with-eureka: true #是否需要从eureka上获取注册信息 fetch-registry: true #Gateway日志级别 logging: level: org.springframework.cloud.gateway: debug
路由日志追踪RouteLogFilter
package com.thinkingcao.gateway.filter;
import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.route.Route; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono;
import java.net.URI; import java.util.Collections; import java.util.Set;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.*;
/**
- @desc: gateway log路由
- @author: cao_wencao
- @date: 2020-06-28 13:42
*/ @Slf4j(topic = "RouteLog") @Component public class RouteLogFilter implements GlobalFilter {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { Set<URI> uris = exchange.getAttributeOrDefault(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, Collections.emptySet()); String originalUri = (uris.isEmpty()) ? "Unknown" : uris.iterator().next().toString(); Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); URI routeUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); log.info("【in request 】: " + originalUri + " ," + "【is routed to id】: " + route.getId() + ", uri: " + routeUri); return chain.filter(exchange); }
}
启动类AppGateway
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
- @desc: Api Gateway网关
- @author: cao_wencao
- @date: 2020-06-23 13:07
*/ @SpringBootApplication @EnableEurekaClient public class AppGateway {
public static void main(String[] args) { SpringApplication.run(AppGateway.class, args); }
}
3. Product商品服务
pom.xml
org.example springcloud-product-service 1.0-SNAPSHOT springcloud-product-service 商品服务 org.springframework.boot spring-boot-starter-parent 2.0.3.RELEASE <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> org.springframework.cloud spring-cloud-dependencies ${ spring-cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-actuator org.projectlombok lombok true org.springframework.boot spring-boot-maven-plugin application.yml
server: port: 8016 #定义服务名称 spring: application: name: product-service #在此指定服务注册中心地址,将当前服务注册到eureka注册中心上 eureka: client: service-url: defaultZone: http://127.0.0.1:8888/eureka #启动注册操作,该值默认为true。若设置为fasle将不会启动注册操作。是否需要去检索寻找服务,默认是true register-with-eureka: true #是否需要从eureka上获取注册信息 fetch-registry: true
Product服务接口
package com.thinkingcao.product.controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap; import java.util.Map;
/**
- @desc: 商品服务
- @author: cao_wencao
- @date: 2020-06-28 10:34
*/ @RestController @RequestMapping("/product") public class ProductController {
@RequestMapping("/getProductInfo") public Map<String,String> getProductInfo(){ Map<String,String> productsMap = new HashMap<>(); productsMap.put("productNo","111"); productsMap.put("productName","荔枝"); productsMap.put("productDesc","一骑红尘妃子笑,无人知是荔枝来"); productsMap.put("productPrice","11.0"); productsMap.put("productWeight","10.0"); return productsMap; }
}
启动类
package com.thinkingcao.product;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
- @desc: 商品服务
- @author: cao_wencao
- @date: 2020-06-23 13:17
*/ @SpringBootApplication @EnableEurekaClient public class AppProduct{
public static void main(String[] args) { SpringApplication.run(AppProduct.class,args); }
}
七. Gateway十一种路由谓词工厂
通过查阅Gateway
官方文档,可发现Gateway
提供了11种路由谓词工厂来匹配请求,通过谓词的匹配转发到具体的微服务实例。
7.1.后路线谓词工厂
路由谓词工厂采用一个参数,即日期时间。该谓词匹配在指定日期时间之后发生的请求。以下示例配置了路由后谓词:
例子1. application.yml
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
这条路线符合2017年1月20日17:42山区时间(丹佛)之后的任何请求。
7.2.之前路线谓词工厂
路由前谓词工厂采用一个参数datetime
,即日期时间。该谓词匹配在指定日期时间之前发生的请求。以下示例配置了路由前谓词:
例子2. application.yml
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
这条路线符合2017年1月20日山区时间(丹佛)之前的任何请求。
7.3. 路由谓词间工厂
路线谓词工厂之间有两个参数,datetime1
和datetime2
。此谓词匹配之后datetime1
和之前发生的请求datetime2
。该datetime2
参数必须是后datetime1
。以下示例配置了路由之间的谓词:
例子3. application.yml
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
该路线与2017年1月20日山区时间(丹佛)之后和2017年1月21日17:42山区时间(丹佛)之前提出的任何请求相匹配。这对于维护时段可能很有用。
7.4.Cookie路线谓词工厂
cookie路由谓词工厂采用两个参数,即cookie名称和正则表达式。该谓词匹配具有给定名称且其值与正则表达式匹配的cookie。以下示例配置cookie路由谓词工厂:
例子4. application.yml
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
此路由匹配具有名称为chocolate
与ch.p
正则表达式匹配的cookie的请求。
7.5.标头路由谓词工厂
标头路由谓词工厂使用两个参数,标头名称和正则表达式。该谓词与具有给定名称的标头匹配,该标头的值与正则表达式匹配。以下示例配置标头路由谓词:
例子5. application.yml
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
如果请求具有名为X-Request-Id
的标头,且其值与\ d +
正则表达式匹配(即,其值为一个或多个数字),则此路由匹配。
7.6.主机路由谓词工厂
主机路由谓词工厂使用一个参数:主机名模式列表。该模式是带有.分隔符的Ant样式的模式。谓词与Host匹配模式的标头匹配。以下示例配置主机路由谓词:
例子6. application.yml
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
{sub}.myhost.org
还支持URI
模板变量(例如)。
如果请求的Host
标头值为www.somehost.org
或beta.somehost.org
或,则此路由匹配www.anotherhost.org
。
该谓词提取URI模板变量(例如sub
,在前面的示例中定义的)作为名称和值的映射,并ServerWebExchange.getAttributes()
使用中定义的键将其放在中ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
。这些值可供工厂使用GatewayFilter
。
7.7.方法路线谓词工厂
方法路由谓词工厂采用一个或多个参数:要匹配的HTTP方法。以下示例配置方法route谓词:
例子7. application.yml
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
如果请求方法是GET
或POST
,则此路由匹配。
7.8.路径路线谓词工厂
路径路由谓词工厂具有两个参数:Spring PathMatcher
模式列表和一个称为matchOptionalTrailingSeparator
的可选标志。。以下示例配置路径路由谓词:
例子8. application.yml
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Path=/red/{
segment},/blue/{
segment}
如果请求路径为例如/red/1
或/red/blue
或/blue/green
,则他的路线匹配。该谓词提取URI模板变量(例如segment
,在前面的示例中定义的)作为名称和值的映射,作为名称和值的映射,并使用ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
中定义的键将其放置在ServerWebExchange.getAttributes()
中。这些值随后可供GatewayFilter
使用。
可以使用实用程序方法(称为get
)来简化对这些变量的访问。下面的示例演示如何使用该get
方法:
Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);
String segment = uriVariables.get("segment");
7.9.查询路由谓词工厂
查询路由谓词工厂采用两个参数:required param
和optional regexp
。以下示例配置查询路由谓词:
例子9. application.yml
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
如果请求包含green
查询参数,则前面的路由匹配。
application.yml
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.
如果请求包含一个前述路线匹配red
,其值相匹配的查询参数gree.
的regexp
,所以green
和greet
将匹配。
7.10.RemoteAddr路由谓词工厂
RemoteAddr
路由谓词工厂采用CIDR
表示法(IPv4
或IPv6
)字符串的列表(最小大小1),例如192.168.0.1/16
(其中192.168.0.1
IP地址和16子网掩码)。下面的示例配置一个RemoteAddr
路由谓词:
例子10. application.yml
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
如果请求的远程地址是例如192.168.1.10
,则此路由匹配。
7.11.重量路线谓词工厂
权重路由谓词工厂采用两个参数:group
和weight
。权重是按组计算的。以下示例配置权重路由谓词:
例子11. application.yml
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
这条路线会将约80%
的流量转发至weighthigh.org
,并将约20%
的流量转发至weightlow.org
。
八. Gateway配置支持跨域和负载均衡
在微服务架构中,一般都是后端使用SpringCloud
技术栈、前端使用Vue等技术,整个项目在架构上以前后端分离的形式开发和部署,如果部署过程中前端和后端不在同一台服务器上或者端口不同、域名不同、或者协议不同,由于浏览器的同源策略
,此时前端访问后端接口时,就会产生跨域问题。跨域问题属于浏览器策略问题,经常前端和后端工程师为此问题争论不休,其实应当由后端来解决服务请求跨域问题,在GateWay
网关中支持跨域解决,在微服务中将跨域放在微服务统一入口解决比较好,因此建议放在网关层解决。
1. Gateway中解决跨域在yml中配置如下
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowCredentials: true #允许访问携带cookie的
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"
maxAge: 3628800
2. Gateway支持负载均衡
以lb://
开头的请求, 会被全局过滤器RetryLoadBalancerClientFilter
拦截并进行负载均衡处理, 所有的动态路由都会自动负载均衡。
九. 谓词工厂实践
9.1. Path谓词匹配
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- Path=/product/** # 匹配对应 URL 的请求,将匹配到的请求追加在目标 URI 之后
请求URL网关: http://127.0.0.1:8015/product/getProductInfo
,将会路由至Product商品服务接口: http://127.0.0.1:8016/product/getProductInfo
IDEA控制台打印日志如下:
2020-06-30 10:44:04.994 DEBUG 21632 --- [ctor-http-nio-5] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://127.0.0.1:8015/product/getProductInfo] to Route{
id='product-service', uri=lb://product-service, order=0, predicate=org.springframework.cloud.gateway.support.ServerWebExchangeUtils$$Lambda$484/760135579@50a80b3a, gatewayFilters=[]}
2020-06-30 10:44:04.995 DEBUG 21632 --- [ctor-http-nio-5] o.s.c.g.handler.FilteringWebHandler : Sorted gatewayFilterFactories: [OrderedGatewayFilter{
delegate=GatewayFilterAdapter{
delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@633a2e99}, order=-2147482648}, OrderedGatewayFilter{
delegate=GatewayFilterAdapter{
delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@37ed010a}, order=-1}, OrderedGatewayFilter{
delegate=GatewayFilterAdapter{
delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter@5a82ebf8}, order=0}, OrderedGatewayFilter{
delegate=GatewayFilterAdapter{
delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@367d2816}, order=10000}, OrderedGatewayFilter{
delegate=GatewayFilterAdapter{
delegate=org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@7d5508e0}, order=10100}, OrderedGatewayFilter{
delegate=GatewayFilterAdapter{
delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@68fe48d7}, order=2147483646}, GatewayFilterAdapter{
delegate=com.thinkingcao.gateway.filter.RouteLogFilter@7c551ad4}, OrderedGatewayFilter{
delegate=GatewayFilterAdapter{
delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@554cd74a}, order=2147483647}, OrderedGatewayFilter{
delegate=GatewayFilterAdapter{
delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@5b84f14}, order=2147483647}]
2020-06-30 10:44:05.008 INFO 21632 --- [ctor-http-nio-5] RouteLog : 【in request 】: lb://product-service:8015/product/getProductInfo ,【is routed to id】: product-service, uri: http://DESKTOP-JBFTEEM:8016/product/getProductInfo
2020-06-30 10:44:06.231 WARN 21632 --- [ctor-http-nio-5] .a.w.r.e.DefaultErrorWebExceptionHandler : Failed to handle request [GET http://127.0.0.1:8015/favicon.ico]: Response status 404
9.2. After谓词匹配
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- After=2020-07-01T00:00:00+08:00[Asia/Shanghai] # 请求时间在 2020年7月1日0点0分0秒之后的所有请求都转发到商品服务实例product-service 上
解释: 请求时间在 2020年7月1日0点0分0秒之后的所有请求都转发到商品服务实例product-service 上,+08:00是指时间和UTC时间相差八个小时,时间地区为Asia/Shanghai。。
9.3. Before谓词匹配
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- Before=2020-07-01T00:00:00+08:00[Asia/Shanghai] # 请求时间在 2020年7月1日0点0分0秒之前的所有请求都转发到商品服务实例product-service 上
解释: 请求时间在 2020年7月1日0点0分0秒之前的所有请求都转发到商品服务实例product-service 上,+08:00是指时间和UTC时间相差八个小时,时间地区为Asia/Shanghai。。
9.4. Between谓词匹配
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- Between=2020-07-01T00:00:00+08:00[Asia/Shanghai] , 2020-08-01T00:00:00+08:00[Asia/Shanghai] # 请求时间在 2020年7月1日0点0分0秒到 2020年8月1日0点0分0秒之间的所有请求都转发到商品服务实例product-service 上。
解释: 请求时间在 2020年7月1日0点0分0秒到 2020年8月1日0点0分0秒之间的所有请求都转发到商品服务实例product-service 上,+08:00是指时间和UTC时间相差八个小时,时间地区为Asia/Shanghai。。
9.5. Cookie谓词匹配
Cookie Route Predicate 可以接收两个参数,一个是 Cookie name ,一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- Cookie=sessionId, 111111 # Cookie name = sessionId ,Cookie value= 111111
解释: 上面Cookie
谓词匹配中指定了Cookie
的name
为sessionId
,Cookie
的value
为111111
,所以只有当请求的请求头header中携带 sessionId =111111
时才进行路由。
9.6. Header谓词匹配
Header Route Predicate和Cookie一样,也是接收两个参数,一个 header 中属性名称,另外一个header值,该值可以是一个正则表达式,当此断言匹配了请求的header名和值时,断言通过,进入到router的规则中去。
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- Header=X-Request-Id, \d+ # X-Request-Id=[数字]
解释: 上面路由的含义是header
中要存在X-Request-Id=[数字]
的信息,所以只有当请求的请求头header
中携带X-Request-Id=[数字]
时才进行路由。
注意: 如果将参数"X-Request-Id:111111"
改为"X-Request-Id:aaaa"
等任意一个非数字字符串时,再次执行时,返回404
证明没有匹配。
9.7. Host谓词匹配
Host Route Predicate 路由断言工厂接受一组参数,一组匹配的域名列表hostname,如果与多个host匹配则用逗号分隔即可。它可以使用. *
等去匹配host
。这个参数会匹配请求头中的host
的值,一致会被路由。
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- Host=**.baidu.com # 使用 . * 去匹配多个host
解释: 如果请求头host为Host: www.baidu.com
或Host: md.baidu.com
,都可以匹配到 host_route
路由,根据 . *
去匹配host
后缀。
9.8. Method谓词匹配
Method
谓词表示可以接收一个参数,就是请求方式 POST
、GET
、PUT
、DELETE
等不同的请求方式来进行匹配路由。
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- Method=GET # 所有以GET方式的请求都会被路由到product-service服务。
解释: 上面- Method=GET
表示所有只要以GET
方式的请求,就都会被路由到product-service
服务。
9.9. Query谓词匹配
Query Route Predicate
谓词支持接收两个参数,一个是属性名[必填],一个是属性值[选填],属性值可以是正则表达式。
只填参数名称
#定义服务名称 spring: application: name: api-gateway # 应用名称 cloud: gateway: discovery: locator: # 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。 enabled: true # 是否开启基于服务发现的路由规则 lower-case-service-id: true # 是否将服务名称转小写 # 路由规则 routes: - id: product-service # 路由 ID,唯一 uri: lb://product-service # 目标 URI,路由到微服务的地址 predicates: # 断言(判断条件) - Query=name
解释: 上面- Query=name
中,只填了必填项属性名称,不填选填项参数值后,只要请求中包含name
属性的参数即可匹配路由,不带 name
参数则不会匹配。。
填参数名称和参数值
#定义服务名称 spring: application: name: api-gateway # 应用名称 cloud: gateway: discovery: locator: # 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。 enabled: true # 是否开启基于服务发现的路由规则 lower-case-service-id: true # 是否将服务名称转小写 # 路由规则 routes: - id: product-service # 路由 ID,唯一 uri: lb://product-service # 目标 URI,路由到微服务的地址 predicates: # 断言(判断条件) - Query=name, Thinkingcao #匹配请求中包含参数的属性名称是
name
,并且参数值是以Thinkingcao
开头的字符串
解释: 上面- Query=name, Thinkingcao
中,只要当请求中包含参数的属性名称是name
,并且参数值是以 Thinkingcao
开头字符串才会进行匹配和路由。
9.10. RemoteAddr谓词匹配
RemoteAddr Route Predicate
路由断言工厂支持通过设置某个ip
区间号段的请求才会路由, 接受 cidr 符号(IPv4 或 IPv6 )字符串的列表(最小大小为1),例如 192.168.10.11/18 (其中 192.168.10.11 是 IP 地址,18 是子网掩码)
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- RemoteAddr=192.168.10.1/24 #远程请求IP地址满足192.168.10网段即可匹配
解释: - RemoteAddr=192.168.10.1/24
表示只要远程请求地址的IP在网段192.168.10
间,则匹配路由,例如,我的服务器主机请求的IP地址是: 192.168.10.3
。
9.11. Weight谓词匹配
Weight Route Predicate
权重路由谓词工厂支持接收两个参数: group和weight, 权重是按组计算的
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
#商品服务1权重为8
- id: product-service1
uri: lb://product-service1
predicates:
- Path=/product/**
- Weight=product-service1, 8
#商品服务2权重为2
- id: product-service2
uri: lb://product-service2
predicates:
- Path=/product/**
- Weight=order-service2, 2
解释: 当请求商品服务http://127.0.0.1:8015/product/getProductInfo
时,gateway
网关路由匹配,根据权重分配比例,会将80%的流量转发至product-service1
,并将约20%的流量转发至product-service2
。
9.12. 所有谓词组合使用
- 当多个谓词组合一起使用的时候,必须所有谓词条件都满足,才可匹配路由;
- 当一个请求满足多个路由的谓词匹配条件时,该请求只会被放在第一一个所的路由进行转发。
#定义服务名称
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。
enabled: true # 是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
# 路由规则
routes:
- id: product-service # 路由 ID,唯一
uri: lb://product-service # 目标 URI,路由到微服务的地址
predicates: # 断言(判断条件)
- Host=**.thinkingcao.top
- Path=/headers
- Method=GET
- Header=X-Request-Id, \d+
- Query=name, Thinkingcao.
- Query=name
- Cookie=sessionId, 111111
- After=2020-07-01T00:00:00+08:00[Asia/Shanghai]
解释: 当上述多个谓词组合一起使用的时候,必须所有谓词条件都满足,才可匹配路由,默认当满足多个谓词时,默认被第一个所匹配的进行转发。
十. 源码
源码:https://github.com/Thinkingcao/SpringCloudLearning/tree/master/springcloud-gateway
本文同步分享在 博客“Thinkingcao”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。