一.为什么需要网关
网关是微服务架构中的统一入口组件,解决了分布式系统中的核心痛点:
二.搭建网关服务的步骤:
1.创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2023.0.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>4.1.4</version>
</dependency>
启动类上加上@EnableDiscoveryClient
- 服务注册:让当前微服务自动向注册中心(如 Eureka、Nacos、Consul 等 )注册自身实例信息(IP、端口、服务名等 )。
- 服务发现:使微服务能从注册中心查询其他服务的实例信息,结合
@LoadBalanced
等可实现基于服务名的调用、负载均衡。
2.编写路由配置及nacos地址
配置文件规范(推荐单文件 + Profile 分离),application.yml和application-route.yml
application.yml
spring:
profiles:
include: route # 引入名为"route"的配置文件(用于分离路由配置,提升可读性)
application:
name: gateway # 应用名称,在服务发现中心显示为"gateway"
cloud:
nacos:
discovery:
server-addr: http://192.168.88.1:8848 # Nacos服务发现地址,用于注册网关自身及发现其他微服务
server:
port: 80 # 网关服务端口,通过80端口对外提供服务
其他微服务的配置进行服务注册
启动后nacos中会生成两个服务
配置路由规则application-route.yml(路由配置示例)
spring:
cloud:
gateway:
routes:
- id: itxb-route # 路由唯一标识,建议使用有业务含义的名称(命名随意)
uri: lb://itxb # 负载均衡到名为"itxb"的微服务(通过服务发现中心获取实例地址---nacos中的服务名)
3.lb是负载均衡需要加上依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>4.1.3</version>
</dependency>
4.GateWay网关的工作流程架构图
三.路由断言工厂Route Predicate Factory
1.什么是断言工厂
- 谓词工厂:将配置文件中的字符串规则转换为路由匹配条件,本质是实现了
RoutePredicateFactory
接口的组件。 - 示例:
Path=/api/itxb/**
由PathRoutePredicateFactory
处理,判断请求路径是否匹配。
2. 短写法与长写法对比
类型 | 配置示例(匹配请求参数 q=haha) | 对应工厂类 |
---|---|---|
短写法 | - Query=q,haha | QueryRoutePredicateFactory |
长写法 |
| QueryRoutePredicateFactory |
3.自定义断言(自定义一个以 VIP 断言工厂为例)
首先创建一个类VipRoutePredicateFactory(用于根据请求头参数判断是否为VIP请求)
/**
* VIP路由谓词工厂 - 用于根据请求头参数判断是否为VIP请求
* 示例配置:- Vip=user,VIP123
*/
@Component
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {
/**
* 定义快捷写法的参数顺序--短写法
* 如:- Vip=param,value
*/
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("param", "value");
}
/**
* 应用谓词逻辑:判断请求头参数是否匹配
* @param config 配置参数(包含要检查的请求头参数名和目标值)
* @return 谓词函数,返回true时路由生效
*/
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
//从请求头中获取参数
String param = exchange.getRequest().getHeaders().getFirst(config.getParam());
//判断参数是否匹配
return param != null && param.equals(config.getValue());
}
};
}
/**
* 配置参数类 - 定义可配置的属性
* 需通过@Validated进行参数校验
*/
@Validated //会触发 Spring 对该类中的字段校验注解(如 @NotEmpty、@NotNull
public static class Config {
@NotEmpty
private String param;
@NotEmpty
private String value;
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public VipRoutePredicateFactory() {
super(Config.class);
}
}
配置路由规则使用
正确访问
现在user=zhangsan
四.过滤器工厂
这是 Spring Cloud Gateway 过滤器(Filter) 的配置与执行流程说明图
1. 过滤器分类与对比
类型 | 定义方式 | 作用范围 | 典型场景 |
---|---|---|---|
路由过滤器 | 配置文件定义 | 仅当前路由生效 | 路径重写、请求头修改 |
全局过滤器 | 代码实现(接口) | 所有路由生效 | 权限校验、响应时间记录 |
2.过滤器的作用:
(1).对路由的请求或响应做加工处理,比如添加请求头配置
(2).在路由下的过滤器只对当前路由的请求生效
现在重写路径请求(前端如果过来的是/api/itxb/ab->后端需要有这个路径,现在加上过去只需要/itxb/ab)
Spring提供了多种不同的路由过滤器工厂
过滤器工厂 | 作用 | 参数 |
---|---|---|
AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
AddResponseHeader | 为原始响应添加Header | Header的名称及值 |
DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 |
Hystrix | 为路由引入Hystrix的断路器保护 | HystrixCommand 的名称 |
FallbackHeaders | 为fallbackUri的请求头中添加具体的异常信息 | Header的名称 |
PrefixPath | 为原始请求路径添加前缀 | 前缀路径 |
PreserveHostHeader | 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host | 无 |
RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的url |
RemoveHopByHopHeadersFilter | 为原始请求删除IETF组织规定的一系列Header | 默认就会启用,可以通过配置指定仅删除哪些Header |
RemoveRequestHeader | 为原始请求删除某个Header | Header名称 |
RemoveResponseHeader | 为原始响应删除某个Header | Header名称 |
RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 |
RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正则表达式,重写后的值 |
SaveSession | 在转发请求之前,强制执行WebSession::save 操作 | 无 |
secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
SetPath | 修改原始的请求路径 | 修改后的路径 |
SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 |
StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
Retry | 针对不同的响应进行重试 | retries、statuses、methods、series |
RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large | 请求包大小,单位为字节,默认值为5M |
ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 |
ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
Default | 为所有路由添加过滤器 | 过滤器工厂名称及值 |
3.默认过滤器
4.全局过滤器:
全局过滤器的作用也是处理一来和微服务响应与GatewayFilter的作用一样。龙川评入以X天R区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现定义方式是实现GlobalFilter接口。
全局过滤器示例(响应时间记录)
/**
* 全局过滤器,用于记录请求的执行时间
* 实现了GlobalFilter接口,会对所有经过网关的请求生效
* 实现了Ordered接口,用于指定过滤器的执行顺序
*/
@Component
@Slf4j
public class RtGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
/**
* 过滤器核心方法,处理请求和响应
* @param exchange 服务器Web交换对象,包含请求和响应
* @param chain 过滤器链,用于调用下一个过滤器
* @return 响应完成的Mono对象
*/
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String url= request.getURI().toString();
long startTime = System.currentTimeMillis();
log.info("请求{}开始:时间{}",url,startTime);
/**
* 记录请求开始时间
* 调用过滤器链,处理请求
* 使用doFinally方法,在请求完成后记录请求结束时间
**/
Mono<Void>filter=chain.filter(exchange).
doFinally(signalType -> {
long endTime = System.currentTimeMillis();
log.info("请求{}结束:时间{}",url,endTime);
log.info("请求{}耗时:{}ms",url,endTime-startTime);
});
return filter;
}
/**
* 获取过滤器的执行顺序
* 值越小,优先级越高
* @return 执行顺序值,这里设置为0
*/
@Override
public int getOrder() {
return 0;
}
}
请求后控制台打印
5.自定义过滤器工厂(一次性令牌生成)
/**
* 一次性令牌网关过滤器工厂
* 用于在每个响应中添加一个一次性令牌,可以是UUID或其他自定义格式
* 继承自AbstractNameValueGatewayFilterFactory,支持配置令牌的名称和值类型
*/
@Slf4j
@Component
public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
public OnceTokenGatewayFilterFactory() {
log.info("OnceTokenFilterFactory initialized");
}
/**
* 应用过滤器配置并创建实际的过滤器实例
* @param config 包含令牌名称和值类型的配置
* @return 配置好的网关过滤器
*/
@Override
public GatewayFilter apply(NameValueConfig config) {
return new GatewayFilter() {
/**
* 过滤器的核心处理逻辑
* 在响应返回前添加一次性令牌到响应头中
* @param exchange 服务器Web交换对象,包含请求和响应
* @param chain 过滤器链,用于调用下一个过滤器
* @return 响应完成的Mono对象
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 在响应处理完成后执行令牌添加逻辑
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 获取响应对象和响应头
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
// 获取配置的值类型
String value = config.getValue();
// 如果配置的值类型为"uuid",则生成一个UUID作为令牌值
if ("uuid".equalsIgnoreCase(value)) {
value = UUID.randomUUID().toString();
}
// 将生成的令牌添加到响应头中
headers.add(config.getName(), value);
}));
}
};
}
}
路由配置并且使用
全局过滤器和GatewayFilter一样都可以对进入网关的请求和微服务的响应做加工处理 GatewayFilter网关过滤器:通过配置文件定义所以处理的逻辑是固定的且只有默认过滤器对所有路由请求生效
GLobalFilter全局过滤器:可以编写代码做自己的业务逻辑,如 登录状态判断,权限校验,请求限流 等,对所有的路由请求都生效
五.过滤器链的执行顺序
过滤器执行顺序
每个过滤器都要设置一个 int
类型的 order
值,order
值越小,优先级越高,执行越靠前。
GlobalFilter
:需通过实现Ordered
接口,或添加@Order
注解来指定order
值,由开发者自定义。- 路由过滤器、
defaultFilter
:order
由 Spring 管控,默认按声明顺序从1
开始递增。
若多个过滤器 order
值相同,执行顺序为:defaultFilter
→ 路由过滤器 → GlobalFilter
。
六.网关的cors跨域配置
1.跨域:域名不一致就是跨域,主要包括:
域名不同:www.taobao.com和www.taobao.org和 www,jd.com 和 miaosha.jd.com域名相同,
端口不同:localhost:8080和localhost8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截,导致前端无法正常调用后端接口
传统的解决方案:在每个 Controller
上添加 @CrossOrigin
注解,需逐个配置,Controller 数量多时代码冗余、维护复杂
解决方案:CORS