7.GateWay网关
7.1. 简介
- Spring Cloud Gateway是Spring Cloud生态系统中的网关组件,用于提供路由、过滤、限流等功能。Spring Cloud Gateway基于Spring6、Spring Boot3和Project Reactor等技术。它旨在为微服务架构提供简单有效的统一API路由管理。
- 核心是一系列的过滤器,通过这些过滤器可以将客户端的请求转发到对应的微服务。是加在整个微服务最前沿的防火墙和代理期,隐藏微服务节点IP端口信息,加强安全保护。
- Spring Cloud Gateway本身也是微服务,需要注册进服务注册中心。
7.2.网关定位
- GateWay VS Nginx:Nginx位于客户端和服务器之间,用于反向代理和负载均衡,而Spring Cloud Gateway位于微服务和Nginx之间,用于路由、过滤和限流。
7.3. GateWay三大核心
- 路由:路由是GateWay中最基础的功能,它将客户端的请求转发到对应的微服务。由ID、URI、断言、过滤器组成,如果断言为true则匹配该路由。
- 断言:断言是GateWay中用于判断请求是否满足特定条件的机制,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),只有满足条件的请求才会被转发到对应的微服务。
- 过滤:过滤是GateWay中用于处理请求和响应的机制,它可以在请求被路由前或者之后对请求进行处理。
7.4. GateWay工作流程
- 客户端向GateWay发送请求,GateWay根据请求的路径和路由规则,将请求发送到WebHandler。Handler通过指定的过滤器链来将请求发送给对应的微服务。
- 过滤器可能会在发送代理之前或之后执行业务逻辑。
- "pre"类型的过滤可以做参数校验、权限校验、流量监控、日志输出、协议转换等,
- "post"类型的过滤器可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
7.5. GateWay路由配置
- 配置文件
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: payment_route1
# uri: http://localhost:9001
uri: lb://nacos-pay-provider #动态路由,直接寻找微服务,支持负载均衡
predicates:
- Path=/pay/gateway/get/**
- id: payment_route2
# uri: http://localhost:9001
uri: lb://nacos-pay-provider
predicates:
- Path=/pay/gateway/info/**
- openfeign配置
@FeignClient(value = "cloud-gateway") //微服务对应网关
public interface PayFeignApi {
@GetMapping("/pay/nacos/{id}")
public ResultData getPayInfo(@PathVariable("id") Integer id);
@GetMapping(value="/pay/micrometer/{id}")
public String myMicrometer(@PathVariable("id") Integer id);
@GetMapping("/pay/gateway/get/{id}")
public ResultData getById(@PathVariable("id") Integer id);
@GetMapping("/pay/gateway/info")
public ResultData getInfo();
}
客户端发送请求后,openfeign会寻找对应网关,成功访问9527端口下的 /pay/gateway/get/** 和 /pay/gateway/info/**,请求会被转发到9001端口。
7.6. GateWay断言
- Path:路径断言,用于匹配请求路径。例如,Path=/pay/gateway/get/**表示匹配所有以/pay/gateway/get/开头的请求路径。
- After:时间断言,用于匹配请求时间。例如,After=2022-01-01T00:00:00.000+0000表示匹配在2022年1月1日00:00:00之后发出的请求。
- Before:时间断言,用于匹配请求时间。例如,Before=2022-01-01T00:00:00.000+0000表示匹配在2022年1月1日00:00:00之前发出的请求。
- Cookie:Cookie断言,用于匹配请求中的Cookie。例如,Cookie=username,zzyy表示匹配Cookie中包含username=zzyy的请求。
- Header: Header断言,用于匹配请求头。例如,Header=X-Request-Id, \d+表示匹配请求头中包含X-Request-Id且其值为数字的请求。
- Host: 匹配请求的主机名。例如,Host=**.zzyy.com表示匹配所有以.zzyy.com结尾的主机名。
- Query: 匹配请求的查询参数。例如,Query=username, \d+表示匹配查询参数中包含username=整数的请求。例如请求为/pay/gateway/get/1?username=1,则匹配成功。
- RemoteAddr: 匹配请求的远程地址。例如,RemoteAddr=192.168.1.1/24表示匹配来自192.168.1.0/24网段的请求。
- Metthod: 匹配请求的方法。例如,Method=GET表示匹配GET请求。
示例代码:
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: payment_route1
# uri: http://localhost:9001
uri: lb://nacos-pay-provider #动态路由,直接寻找微服务,支持负载均衡
predicates:
- Path=/pay/gateway/get/**
- After=2025-02-05T15:08:30.031606300+08:00[Asia/Shanghai]
- Cookie=username,zzyy
- Header=X-Request-Id, \d+
- Host=**.zzyy.com
- Query=username, \d+
- RemoteAddr=192.168.83.1/24
- Method=GET, POST
- id: payment_route2
# uri: http://localhost:9001
uri: lb://nacos-pay-provider
predicates:
- Path=/pay/gateway/info/**
7.7. GateWay自定义断言
7.7.1. 自定义步骤
- 新建类名以RoutePredicateFactory结尾,继承AbstractRoutePredicateFactory
- 重写apply方法
- 新建apply方法所需要的静态内部类MyRoutePredicateConfig,这个是路由断言规则
- 空参构造方法,内部调用super
- 重写apply方法第二版
7.7.2. 代码实现
- 编写自定义断言类
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory {
public MyRoutePredicateFactory() {
super(MyRoutePredicateFactory.Config.class);
}
@Override
public Predicate apply(Config config) {
return new Predicate() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
if (userType == null) {
return false;
}
if (userType.equalsIgnoreCase(config.getUserType())) {
return true;
}
return false;
}
};
}
public static class Config {
@Setter@Getter@NotEmpty
private String userType; //用户等级
}
# 支持短格式书写配置
@Override
public List shortcutFieldOrder() {
return Collections.singletonList("userType");
}
}
- 编写配置文件
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: payment_route1
# uri: http://localhost:9001
uri: lb://nacos-pay-provider #动态路由,直接寻找微服务,支持负载均衡
predicates:
- Path=/pay/gateway/get/**
# - name: My
# args:
# userType: diamond
- My=diamond
7.8. GateWay过滤器
7.8.1. 过滤器的作用
- 过滤器可以在请求被路由之前或之后对请求进行修改,或者对路由的请求响应进行修改。
7.8.2. GateWay内置过滤器
- 过滤器类型 GatewayFilter GlobalFilter 自定义Filter
- 过滤器作用范围 单个路由 全局
7.8.3. GateWay常用内置过滤器
1.RequestHeader:
- AddRequestHeader:添加请求头
- RemoveRequestHeader:移除请求头
- SetRequestHeader:设置请求头
2.RequestParameter:
- AddRequestParameter:添加请求参数
- RemoveRequestParameter:移除请求参数
- SetRequestParameter:设置请求参数
3.ResponseHeader:
- AddResponseHeader:添加响应头
- RemoveResponseHeader:移除响应头
- SetResponseHeader:设置响应头
4.Path:
- PrefixPath:路径前缀
- SetPath:设置替换路径
- RedirectTo:重定向
配置文件:
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: payment_route1
# uri: http://localhost:9001
uri: lb://nacos-pay-provider #动态路由,直接寻找微服务,支持负载均衡
predicates:
- Path=/pay/gateway/get/**
- After=2025-02-05T15:08:30.031606300+08:00[Asia/Shanghai]
- Cookie=username,zzyy
- Header=X-Request-Id, \d+
- Host=**.zzyy.com
- Query=username, \d+
# - RemoteAddr=192.168.83.1/24
- Method=GET, POST
# - name: My
# args:
# userType: diamond
- My=diamond
- id: payment_route2
# uri: http://localhost:9001
uri: lb://nacos-pay-provider
predicates:
- Path=/pay/gateway/info/**
- id: payment_route3
# uri: http://localhost:9001
uri: lb://nacos-pay-provider
predicates:
- Path=/pay/gateway/filter/**
# - Path=/gateway/filter/**
# - Path=/XYZ/abc/{segment}
filters:
- RedirectTo=302,http://www.baidu.com #302错误进行跳转
# - SetPATH=/pay/gateway/{segment} # 上面配置的/XYZ/abc会被替换为当前路径
# - PrefixPath= /pay # 前缀由配置统一管理
# - AddRequestHeader=X-Request-yeffky,yeffky
# - RemoveRequestHeader=cookie
# - SetRequestHeader=X-Request-Id,123456
# - AddRequestParameter=customerId,9527001
# - RemoveRequestParameter=customerName
# - AddResponseHeader=X-Response-Id,BlueResponse
# - RemoveResponseHeader=Content-Type
# - SetResponseHeader=Date,2099-11-11
7.8.4.自定义过滤器
- 自定义全局Filter:统计接口调用耗时
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {
public static final String BEGIN_VISIT_TIME = "begin_visit_time";
@Override
public Mono 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("访问接口参数:" + exchange.getRequest().getURI().getRawQuery());
log.info("访问接口时长:" + (System.currentTimeMillis() - beginVisitTime) + "ms");
log.info("=================");
System.out.println();
}
}));
}
// 数字越小,优先级越高
@Override
public int getOrder() {
return 0;
}
}
- 自定义条件Filter
- 配置类
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory {
public MyGatewayFilterFactory() {
super(MyGatewayFilterFactory.Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
System.out.println("进入了自定义网关过滤器:" + config.getStatus());
if (request.getQueryParams().containsKey(config.getStatus())) {
return chain.filter(exchange);
} else {
exchange.getResponse().setStatusCode(HttpStatus.BAD_GATEWAY);
return exchange.getResponse().setComplete();
}
}
};
}
public static class Config {
@Getter @Setter
private String status;
}
@Override
public List shortcutFieldOrder() {
return Arrays.asList("status");
}
}
-yml配置
spring:
cloud:
gateway:
routes:
- id: payment_route3
# uri: http://localhost:9001
uri: lb://nacos-pay-provider
predicates:
- Path=/pay/gateway/filter/**
filters:
- My=yeffky #需要有yeffky这个键,而非键值对为"status: yeffky"