Gateway 能够实现多种网关功能,比如:路由转发、断言、过滤器、熔断、限流、降级、自定义谓词配置、自定义过滤器 等等多种功能。
1、网关断言
断言的英文是 Predicate,也可以翻译成谓词。主要的作用就是进行条件判断,可以在网关中实现多种条件判断,只有所有的判断结果都通过时,也就是所有的条件判断都返回 true,才会真正的执行路由功能
1.1、Gateway 内置断言
Gateway 包括许多内置的断言工厂,所有这些断言都与 HTTP 请求的不同属性匹配。
1.1.1、基于日期时间类型的断言
基于日期时间类型的断言根据时间做判断,主要有三个:
- AfterRoutePredicateFactory: 接收一个日期时间参数,判断当前请求的日期时间是否晚于指定的日期时间。
- BeforeRoutePredicateFactory: 接收一个日期时间参数,判断当前请求的日期时间是否早于指定的日期时间。
- BetweenRoutePredicateFactory: 接收两个日期时间参数,判断当前请求的日期时间是否在指定的时间时间段内
如:
- After=2022-05-10T23:59:59.256+08:00[Asia/Shanghai]
1.1.2、基于远程地址的断言
RemoteAddrRoutePredicateFactory:接收一个 IP 地址段:判断发出请求的客户端的 IP 地址是否在指定的 IP 地址段内
如:
- RemoteAddr=192.168.0.1/24
1.1.3、基于 Cookie 的断言
CookieRoutePredicateFactory:接收两个参数, Cookie 的名称和一个正则表达式。:判断请求的 Cookie 是否具有给定名称且值与正则表达式匹配
如:
- Cookie=name, zzc.
1.1.4、基于 Header 的断言
HeaderRoutePredicateFactory:接收两个参数,请求 Header 的名称和正则表达式。:判断请求 Header 中是否具有给定的名称且值与正则表达式匹配。
如:
- Header=X-Request-Id, \d+
1.1.5、基于 Host 的断言
HostRoutePredicateFactory:接收一个参数,这个参数通常是主机名或者域名的模式【**.baidu.com
】:判断发出请求的主机是否满足匹配规则。
如:
- Host=**.baidu.com
1.1.6、基于 Method 请求方法的断言
MethodRoutePredicateFactory:接收一个参数,判断请求的类型是否跟指定的类型匹配,通常指的是请求方式。例如,POST、GET、PUT 等请求方式
如:
- Method=GET
1.1.7、基于 Path 请求路径的断言
PathRoutePredicateFactory:接收一个参数,判断请求的链接地址是否满足路径规则,通常指的是请求的 URI 部分
如:
- Path=/order/{segment}
1.1.8、基于 Query 请求参数的断言
QueryRoutePredicateFactory:接收两个参数,请求参数和正则表达式,:判断请求的参数是否具有给定的名称并且参数值是否与正则表达式匹配
如:
- Query=name, zzc.
1.1.9、基于路由权重的断言
WeightRoutePredicateFactory:接收一个[组名,权重]格式的数组,然后对于同一个组内的路由按照权重转发
如:
- id: weight1
uri: http://localhost:8080
predicates:
- Path=/api/**
- Weight=group1,2
filters:
- StripPrefix=1
- id: weight2
uri: http://localhost:8081
predicates:
- Path=/api/**
- Weight=group1,8
filters:
- StripPrefix=1
1.1.10、演示内置断言
- 基于 Path 请求路径的断言判断请求路径是否符合规则
- 基于远程地址的断言判断请求主机地址是否在地址段中
- 并且限制请求的方式为 GET 方式
1、查看本机 IP:
本机的 IP 地址为 192.168.10.9,属于192.168.10.1/24 网段
2、在服务网关模块 shop-gateway
中,将 application.yml
文件备份成 application-sentinel.yml
文件,并将 application.yml
文件中的内容修改成 application-simple.yml
文件中的内容。接下来,在 application.yml
文件中的 spring.cloud.gateway.routes
节点下的 - id: user-gateway
下面进行断言配置,配置后的结果如下所示:
spring:
cloud:
gateway:
routes: # 路由配置【数组】
- id: user-gateway # 路由 id
uri: http://localhost:8060 # 路由地址
order: 1 # 路由排序,数字越大,优先级越低
predicates: # 断言配置/路由转发的条件【数组】
- Path=/server-user/** # 路径匹配:当客户端请求的路径满足 Path 的规则时,进行路由转发操作
- RemoteAddr=192.168.10.1/24
- Method=GET
【注意】:得注释掉 spring.cloud.gateway.discovery.locator.enabled
配置,否则,不生效
3、启动用户微服务、网关微服务
4、通过 http://localhost:10001/server-user/user/get/1001
链接不能正确访问到用户信息:
在浏览器中输入http://192.168.0.27:10001/server-user/user/get/1001
,能够正确获取到用户的信息:
5、停止网关微服务,将基于远程地址的断言配置成 - RemoteAddr=192.168.11.1/24
,也就是将基于
远程地址的断言配置成与我本机 IP 地址不在同一个网段,这样就能演示请求主机地址不在地址段中的情况,修改后的基于远程地址的断言配置如下所示。
- RemoteAddr=192.168.1.1/24
6、重启网关服务,再次访问,发现都不能访问!!
1.2、自定义断言
1.2.1、概述
Gateway 支持自定义断言功能,我们可以在具体业务中,基于 Gateway 自定义特定的断言功能
Gateway 虽然提供了多种内置的断言功能,但是在某些场景下无法满足业务的需要,此时,我们就可以基于 Gateway 自定义断言功能,以此来满足我们的业务场景
1.2.2、实现自定义断言
基于 Gateway 实现断言功能,实现后的效果是在服务网关的 ·application.yml·文件中的spring.cloud.gateway.routes
节点下的 - id: user-gateway
下面进行如下配置:
spring:
cloud:
gateway:
routes: # 路由配置【数组】
- id: user-gateway # 路由 id
uri: http://localhost:8060 # 路由地址
order: 1 # 路由排序,数字越大,优先级越低
predicates: # 断言配置/路由转发的条件【数组】
- Path=/server-user/** # 路径匹配:当客户端请求的路径满足 Path 的规则时,进行路由转发操作
- Name=zzc
通过服务网关访问用户微服务时,只有在访问的链接后面添加 ?name=zzc
参数时才能正确访问用户
微服务
1、在网关服务 shop-gateway
中新建com.zzc.predicate
包,在包下新建 NameRoutePredicateConfig
类,主要定义一个 String 类型的 name
成员变量,用来接收配置文件中的参数,源码如下所示:
public class NameRoutePredicateConfig implements Serializable {
private static final long serialVersionUID = -3289515863427972825L;
private String name;
// getter/setter
}
2、实现自定义断言时,需要新建类继承
org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory
类,在
com.zzc.predicate
包下新建 NameRoutePredicateFactory
类,继承
org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory
类,并覆写相关的方法,源码如下所示:
@Component
public class NameRoutePredicateFactory extends AbstractRoutePredicateFactory<NameRoutePredicateConfig> {
public NameRoutePredicateFactory() {
super(NameRoutePredicateConfig.class);
}
@Override
public Predicate<ServerWebExchange> apply(NameRoutePredicateConfig config) {
return serverWebExchange -> {
String name = serverWebExchange.getRequest().getQueryParams().getFirst("name");
if (StringUtils.isEmpty(name)){
name = "";
}
return name.equals(config.getName());
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("name");
}
}
3、分别启动用户微服务与网关服务,在浏览器中输入http://localhost:10001/server-user/user/get/1001
,如下所示:
4、在浏览器中输入http://localhost:10001/server-user/user/get/1001?name=zzc
,如下所示:
2、网关过滤器
过滤器可以在请求过程中,修改请求的参数和响应的结果等信息
①:按生命周期分:
- 前置过滤器(Pre)
- 后置过滤器(Post)
②:按过滤范围分:
- 局部过滤器(GatewayFilter):某一个路由
- 全局过滤器(GlobalFilter):全部路由
- Pre 前置过滤器:在请求被网关路由之前调用,可以利用这种过滤器实现认证、鉴权、路由等功能,也可以记录访问时间等信息
- Post 后置过滤器:在请求被网关路由到微服务之后执行。可以利用这种过滤器修改 HTTP 的响应 Header 信息,修改返回的结果数据(例如对于一些敏感的数据,可以在此过滤器中统一处理后返回),收集一些统计信息等
- 局部过滤器(GatewayFilter):也可以称为网关过滤器,这种过滤器主要是作用于单一路由或者某个路由分组
- 全局过滤器(GlobalFilter):这种过滤器主要作用于所有的路由
2.1、局部过滤器
Gateway 中内置了很多不同类型的局部过滤器,主要如下所示:
2.1.1、演示内部过滤器
为原始请求添加一个 Header【IP = localhost】,并添加一个参数【name = zzc】,同时修改响应的结果状态,将结果状态修改为1001。
1、在服务网关的 application.yml
文件中的 spring.cloud.gateway.routes
节点下的 - id: user gateway
下面进行如下配置:
spring:
cloud:
gateway:
routes: # 路由配置【数组】
- id: user-gateway # 路由 id
uri: http://localhost:8060 # 路由地址
order: 1 # 路由排序,数字越大,优先级越低
predicates: # 断言配置/路由转发的条件【数组】
- Path=/server-user/** # 路径匹配:当客户端请求的路径满足 Path 的规则时,进行路由转发操作
filters: # 过滤器配置【数组】
- StripPrefix=1 # 去掉前缀:去掉客户端请求的路径中的第一个路径,即去掉 /server-user
- AddRequestHeader=IP,localhost
- AddRequestParameter=name,zzc
- SetStatus=1001
2、在用户微服务的 com.zzc.user.controller.UserController
类中新增 apiFilter1()
方法,如下所示:
@GetMapping(value = "/api/filter1")
public String apiFilter1(HttpServletRequest request, HttpServletResponse response){
log.info("访问了apiFilter1接口");
String ip = request.getHeader("IP");
String name = request.getParameter("name");
log.info("ip = " + ip + ", name = " + name);
return "apiFilter1";
}
3、分别启动用户微服务与网关服务,在浏览器中输入 http://localhost:10001/server-user/user/api/filter1
,如下所示:
2.1.2、自定义局部过滤器
基于 Gateway 自定义局部过滤器实现是否开启灰度发布的功能,整个实现过程如下所示。
1、在服务网关的 application.yml
文件中的 spring.cloud.gateway.routes
节点下的gateway 下面进行如下配置:
spring:
cloud:
gateway:
routes: # 路由配置【数组】
- id: user-gateway # 路由 id
uri: http://localhost:8060 # 路由地址
order: 1 # 路由排序,数字越大,优先级越低
predicates: # 断言配置/路由转发的条件【数组】
- Path=/server-user/** # 路径匹配:当客户端请求的路径满足 Path 的规则时,进行路由转发操作
filters: # 过滤器配置【数组】
- StripPrefix=1 # 去掉前缀:去掉客户端请求的路径中的第一个路径,即去掉 /server-user
- Grayscale=true
2、在网关服务模块 shop-gateway
中新建 com.zzc.filter
包,在包下新建GrayscaleGatewayFilterConfig
类,用于接收配置中的参数,如下所示:
public class GrayscaleGatewayFilterConfig implements Serializable {
private static final long serialVersionUID = 983019309000445082L;
private boolean grayscale;
// getter/setter
}
3、在 com.zzc.filter
包下 GrayscaleGatewayFilterFactory
类,继承org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory
类,主要是实现自定义过滤器,模拟实现灰度发布。代码如下所示:
@Component
public class GrayscaleGatewayFilterFactory extends AbstractGatewayFilterFactory<GrayscaleGatewayFilterConfig> {
public GrayscaleGatewayFilterFactory() {
super(GrayscaleGatewayFilterConfig.class);
}
@Override
public GatewayFilter apply(GrayscaleGatewayFilterConfig config) {
return (exchange, chain) -> {
if (config.isGrayscale()){
System.out.println("开启了灰度发布功能...");
}else{
System.out.println("关闭了灰度发布功能...");
}
return chain.filter(exchange);
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("grayscale");
}
}
4、分别启动用户微服务和服务网关,在浏览器中输入http://localhost:10001/server-user/user/get/1001
,如下所示:
查看下服务网关的终端,发现已经成功输出了如下信息:
开启了灰度发布功能...
2.2、全局过滤器
全局过滤器是一系列特殊的过滤器,会根据条件应用到所有路由中
在 Gateway中内置了多种不同的全局过滤器,如下所示:
2.2.1、演示全局过滤器
1、在服务网关模块 shop-gateway
模块下的 com.zzc.config
包下新建 GatewayFilterConfig
类,并在类中配置几个全局过滤器,如下所示:
@Configuration
@Slf4j
public class GatewayFilterConfig {
@Bean
@Order(-1) // @Order 注解中的数字越小,执行的优先级越高
public GlobalFilter globalFilter() {
return (exchange, chain) -> {
log.info("执行前置过滤器逻辑");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("执行后置过滤器逻辑");
}));
};
}
}
【注意】:注释掉:com.zzc.config.GatewayConfig
2、启动用户微服务与服务网关,在浏览器中访问http://localhost:10001/server-user/user/get/1001
2.2.2、自定义全局过滤器
Gateway 内置了很多全局过滤器,一般情况下能够满足实际开发需要,但是对于某些特殊
的业务场景,还是需要我们自己实现自定义全局过滤器。
实现一个获取客户端访问信息,并统计访问接口时长的全局过滤器
1、在网关服务模块 shop-order
的 com.zzc.filter
包下,新建 GlobalGatewayLogFilter
类,实现 org.springframework.cloud.gateway.filter.GlobalFilter
接口和 org.springframework.core.Ordered
接口,代码如下所示:
@Slf4j
@Component
public class GlobalGatewayLogFilter implements GlobalFilter, Ordered {
// 开始访问时间
private static final String BEGIN_VISIT_TIME = "begin_visit_time";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 先记录下访问接口的开始时间
exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
return chain.filter(exchange).then(Mono.fromRunnable(()->{
Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
if (beginVisitTime != null){
log.info("访问接口主机: " + exchange.getRequest().getURI().getHost());
log.info("访问接口端口: " + exchange.getRequest().getURI().getPort());
log.info("访问接口URL: " + exchange.getRequest().getURI().getPath());
log.info("访问接口URL参数: " + exchange.getRequest().getURI().getRawQuery());
log.info("访问接口时长: " + (System.currentTimeMillis() - beginVisitTime) + "ms");
}
}));
}
@Override
public int getOrder() {
return 0;
}
}
2、启动用户微服务与网关服务,在浏览器中输入 http://localhost:10001/server-user/user/api/filter1?name=zzc
,如下所示:
3、网关熔断机制
熔断机制 在【SpringCloud Alibaba】(九)学习 Gateway 服务网关 一文中就基于 Gateway 整合 Sentinel 实现了。
代码地址
代码已经上传至码云,码云地址
其中,数据库文件位于 db
文件夹下。