Gateway的基本使用
Gateway简介
Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。
优点:
性能强劲:是第一代网关Zuul的1.6倍功能强大:
内置了很多实用的功能,例如转发、监控、限流等
设计优雅,容易扩展
缺点:
其实现依赖Netty与WebFlux,
不是传统的Servlet编程模型,学习成本高
不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行
需要Spring Boot 2.0及以上的版本,才支持
Gateway火速入门
网关模块
导入Gateway依赖
<dependencies>
<!--gateway 网关依赖,内置webflux 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
配置文件
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: api-ce #当前路由的唯一标识,要求唯一
uri: http://localhost:8888 #请求转发的地址
order: 1 #路由的优先级数字越小级别越高
predicates: # 断言(就是路由转发要满足的条件)
- Path=/test/** # 当请求路径满足Path指定的规则时,才进行路由转发
filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 #转发之前去掉1层路径
测试模块
application.properties
server.port=8888
spring.application.name=api-ce
controller
@RestController("/test")
public class TestController {
@RequestMapping("/index")
public String test(){
return "gateway测试";
}
}
Gateway(增强版)
Gateway网关模块和测试模块都导入Nacos依赖,都在启动类加上注解:@EnableDiscoveryClient
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
修改网关的配置文件
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
routes:
- id: api-ce
uri: lb://api-ce # # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/test/**
filters:
- StripPrefix=1
浏览器访问即可:http://localhost:7000/test/index

Gateway核心架构
基本概念
路由(Route) 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。主要定义了下面的几个信息:
id,路由标识符,区别于其他 Route。
uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
predicate,断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
filter,过滤器用于修改请求和响应信息。
gateway执行流程

执行流程: 执行流程大体如下:
Gateway Client向Gateway Server发送请求
请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文
然后网关的上下文会传递到DispatcherHandler,它负责将请求分发给 RoutePredicateHandlerMapping
RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用
如果过断言成功,由FilteringWebHandler创建过滤器链并调用
请求会一次经过PreFilter–微服务–PostFilter的方法,最终返回响应
Gateway内置断言
Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由。
断言就是说: 在 什么条件下 才能进行路由转发
内置路由断言工厂
SpringCloud Gateway包括许多内置的断言工厂,所有这些断言都与HTTP请求的不同属性匹配。具体如下:
基于Datetime类型的断言工厂此类型的断言根据时间做判断,
主要有三个:
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
-After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]
基于远程地址的断言工厂
RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中
-RemoteAddr=192.168.1.1/24
基于Cookie的断言工厂
CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。
判断请求cookie是否具有给定名称且值与正则表达式匹配。
-Cookie=chocolate,ch.
基于Header的断言工厂
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。
判断请求Header是否具有给定名称且值与正则表达式匹配。
-Header=X-Request-Id, \d+
基于Host的断言工厂
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
-Host=**.testhost.org
基于Method请求方法的断言工厂
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
-Method=GET
基于Path请求路径的断言工厂
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
-Path=/foo/{segment}
基于Query请求参数的断言工厂
QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式,
判断请求参数是否具有给定名称且值与正则表达式匹配。
-Query=baz,ba.
基于路由权重的断言工厂
WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
routes:
-id:weight_route1
uri: host1
predicates:
-Path=/product/
-Weight=group3,1
-id: weight_route2
uri: host2
predicates:
-Path=/product/**
-Weight= group3,9
内置路由断言工厂的使用
验证几个内置断言的使用:
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
routes:
- id: api-ce
uri: lb://api-ce # # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/test/**
- Before=2023-11-28T00:00:00.000+08:00 #限制请求时间在2023-11-28之前
- Method=GET #限制请求方式为GET
filters:
- StripPrefix=1
Gateway自定义路由断言
我们来设定一个场景: 假设我们的应用仅仅让age在(min,max)之间的人来访问。
第一步:在yaml中增加配置,- Age=18,60:判断年龄大于18小于60
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
routes:
- id: api-ce
uri: lb://api-ce # # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/test/**
- Age=18,60 # 限制年龄只有在18到60岁之间的人能访问
filters:
- StripPrefix=1
第2步:自定义一个断言工厂, 实现断言方法可以参照:BeforeRoutePredicateFactory这个类,模仿实现
package com.apigateway.factory;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
//泛型 用于接收一个配置类,配置类用于接收中配置文件中的配置
//这是一个自定义的路由断言工厂类,要求有两个
// 1名字必须是配置+RoutePredicateFactory
//2必须继承AbstractRoutePredicateFactory<配置类>
@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {
//构造函数
public AgeRoutePredicateFactory() {
super(AgeRoutePredicateFactory.Config.class);
}
//读取配置文件的中参数值 给他赋值到配置类中的属性上
@Override
public List<String> shortcutFieldOrder() {
//这个位置的循序必须跟配置文件中的值的循序对应
return Arrays.asList("minAge","maxAge");
}
//断言逻辑
@Override
public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
//1.接收前台的age参数
String age = serverWebExchange.getRequest().getQueryParams().getFirst("age");
//2.判断是否为空
if (!StringUtils.isEmpty(age)){
//如果不为空,在进行,路由逻辑判断
int ages=Integer.parseInt(age);
if (ages>config.getMinAge()&& ages<config.getMaxAge()) {
return true;
}else {
return false;
}
}
return false;
}
};
}
//配置类,用于接受配置文件中的对应参数
@Data
@NoArgsConstructor
public static class Config {
private int minAge;
private int maxAge;
}
}
浏览器访问:http://localhost:7000/test/index/?age=19

Gateway过滤器
三个知识点:
1 作用: 过滤器就是在请求的传递过程中,对请求和响应做一些手脚
2 生命周期: Pre Post
3 分类: 局部过滤器(作用在某一个路由上) 全局过滤器(作用全部路由上)
在Gateway中, Filter的生命周期只有两个:“pre” 和 “post”。
PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTPHeader、收集统计信息和指标、将响应从微服务发送给客户端 等。

Gateway 的Filter从作用范围可分为两种: GatewayFilter与GlobalFilter。
GatewayFilter:应用到单个路由或者一个分组的路由上。
GlobalFilter:应用到所有的路由上。
局部过滤器
局部过滤器是针对单个路由的过滤器。
内置局部过滤器 在SpringCloud Gateway中内置了很多不同类型的网关路由过滤器。具体如下:



每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾,这是Spring Cloud Gateway的一个约定,例如 AddRequestHeader 对应的实现类为AddRequestHeaderGatewayFilterFactory 。对于这些过滤器的使用方式可以参考官方文档
内置局部过滤器
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true
routes:
- id: api-ce
uri: lb://api-ce
predicates:
- Path=/test/**
filters:
- StripPrefix=1
- SetStatus=250 # 局部过滤器,修改返回状态
自定义局部过滤器(有误)
第1步:在配置文件中,添加一个Log的过滤器配置
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true
routes:
- id: api-ce
uri: lb://api-ce
predicates:
- Path=/test/**
filters:
- StripPrefix=1
- Log=true,false #自定义,局部过滤器:控制日志是否开启
第2步:自定义一个过滤器工厂,实现方法
//自定义的局部过滤器
@Component
public class LogGatewayFilterFactory
extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {
//构造函数
public LogGatewayFilterFactory() {
super(LogGatewayFilterFactory.Config.class);
}
//读取配置文件中的参数 赋值到配置类中
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("consoleLog", "cacheLog");
}
//过滤器逻辑
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (config.isCacheLog()){
System.out.println("CacheLog开启.....");
}
if (config.isConsoleLog()) {
System.out.println("ConsoleLog开启.....");
}
return chain.filter(exchange);
}
};
}
//配置类接收配置参数
@Data
@NoArgsConstructor
public static class Config{
private boolean consoleLog;
private boolean cacheLog;
}
}
全局过滤器
SpringCloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理如下:

内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们自己编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的权限校验。
开发中的鉴权逻辑:
当客户端第一次请求服务时,服务端对用户进行信息
认证(登录)认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
以后每次请求,客户端都携带认证的token
服务端对token进行解密,判断是否有效。

如上图,对于验证用户是否已经登录鉴权的过程可以在网关统一检验。
检验的标准就是请求中是否携带token凭证以及token的正确性。下面的我们自定义一个GlobalFilter,去校验所有请求的请求参数中是否包含“token”,如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。
//自定义全局过滤器需要实现G1obalFi1ter和ordered接口
@Component
public class AuthG1oba1Fi1ter implements GlobalFilter, Ordered {
//完成逻辑判断
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isBlank(token)){
System.out.println("认证失败");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
//调用chain.filter继续执行
return chain.filter(exchange);
}
//循序,数值越小。优先级越高
@Override
public int getOrder() {
return 0;
}
}