目录
一、Gateway概述
二、Gateway三大核心
三、Gateway工作流程
入门配置
建Module
改POM
写YML
主启动
业务类
测试
四、9527网关进行路由映射
1.编写支付正常业务逻辑
2.9527网关YML新增配置
3.测试1
4.测试2
启动订单微服务测试,看看是否通过网关?
1.修改cloud-api-commons
2.修改cloud-consumer-feign-order88
正确做法
同一家公司自己人,系统内环境,直接找微服务
不同家公司有外人,系统外访问,先找网关再服务
编辑
1.刷新feign接口jar包
2.重启80订单微服务
3.有网关正常success
4.无网关异常
五、GateWay高级特性
Route以微服务名-动态获取服务URI
ReactiveLoadBalancerClientFilter(负载均衡和动态路由)
uri地址修改为动态获取
测试1:
测试2:
Predicate断言(谓词)
简介
内置断言工厂介绍
常见的内置Route Predicate使用
Shortcut Configuration 快捷方式
Fully Expanded Arguments 完全展开
1.After Route Predicate (后路由谓词工厂)
2. Before Route Predicate(前路由谓词工厂)
3. Between Route Predicate(中间路由谓词工厂)
4. Cookie Route Predicate (Cookie路线谓词工厂)
编辑
5. Header Route Predicate(头文件路由谓词工厂)
6. Host Route Predicate (主机路由谓词工厂)
7.Method Route Predicate(方法路由谓语工厂)
8. Path Route Predicate (路径路由谓词工厂)
9. Query Route Predicate(查询路由谓词工厂)
10.RemoteAddr route predicate(远程地址路由谓词工厂)
上述配置小总结
自定义断言,XXXRoutePredicateFactory规则
自定义RoutePredicateFactory实现方式
自定义路由断言规则步骤套路
编写步骤
完整代码
测试1
bug分析
测试2
完整代码02
Filter过滤
概述
作用:
类型:
Gateway内置的过滤器
1.请求头(RequestHeader)相关组
6.1.The AddRequestHeader GatewayFilter Factory(添加请求头网关过滤器工厂)
6.18.The RemoveRequestHeader GatewayFilter Factory(移除请求头网关过滤器工厂)
6.29.The SetRequestHeader GatewayFilter Factory(设置请求头网关过滤器工厂)
2.请求参数(RequestParameter)相关组
6.3 The AddRequestParameter GatewayFilter Factory(添加请求参数网关过滤器工厂)
6.19 The RemoveRequestParamter GatewayFilter Factory(移除请求参数网关过滤器工厂)
请求参数示例(6.3/6.19):
3.回应头(ResponseHeader)相关组
6.4 The AddResponseHeader GatewayFilter Factory(新增响应头网关过滤器工厂)
6.30 The SetResponseHeader GatewayFilter Factory(设置/修改响应头网关过滤器工厂)
6.20 The RemoveResponseHeader GatewayFilter Factory(移除响应头网关过滤器工厂)
请求参数示例(6.4/6.30/6.20):
4.前缀和路径相关组
6.14.The PrefixPath GatewayFilter Factory(前缀路径网关过滤器工厂)
6.29.The SetPath GatewayFilter Factory(设置路径网关过滤器工厂)
6.16.The RedirectTo GatewayFilter Factory(重定向到网关过滤器工厂)
5. 其它
6.38. Default Filters(默认全局过滤器)
本次案例全部YML配置全集
Gateway自定义过滤器
自定义全局Filter
面试题
官方介绍
示例
自定义条件Filter
自定义网关过滤器规则步骤套路
YML 参数配置
测试
相关知识拓展:
概述Gateway三大核心Gateway工作流程入门配置9527网关如何做路由映射GateWay高级特性Gateway整合阿里巴巴Sentinel实现容错
一、Gateway概述
Gateway是在Spring生态系统之上构建的 API网关服务 ,基于Spring6,Spring Boot 3和Project Reactor等技术。它旨在 为微服务架构提供一种简单有效的统一的 API 路由管理方式 ,并为它们提供跨领域的关注点,例如:安全性、监控/度量和恢复能力。
Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul,那就是SpringCloud Gateway一句话:gateway是原zuul1.x版的替代


- 反向代理
- 鉴权流量
- 控制熔断
- 日志监控
Spring Cloud Gateway是一个基于Spring Framework的反向代理和路由器,它可以用于构建微服务架构中的 API 网关。除了反向代理之外,Spring Cloud Gateway还提供了许多功能,包括鉴权、限流、熔断和日志监控等。1.反向代理Spring Cloud Gateway可以作为应用程序的前端,将所有的请求转发到后端的服务实例上。它支持HTTP和WebSocket协议,并且具有高性能和低延迟的特点。通过负载均衡算法,可以在多个后端服务实例之间进行请求的分发。2.鉴权Spring Cloud Gateway提供了灵活的鉴权机制,可以根据请求的路径、方法、头部信息等来进行鉴权控制。可以与Spring Security等安全框架集成,实现基于角色或权限的访问控制。也可以使用自定义过滤器来实现特定的鉴权逻辑,例如基于令牌、API密钥等的验证。3.限流Spring Cloud Gateway支持基于请求速率、并发数、IP地址等进行请求的限流控制。可以使用内置的限流过滤器或者自定义过滤器来实现限流策略。通过限流可以保护后端的服务免受恶意攻击和过载请求的影响。4.熔断Spring Cloud Gateway可以使用熔断器来处理后端服务的故障或异常情况。当后端服务出现错误时,可以通过配置熔断策略,在一定时间内禁止对该服务的请求,从而防止故障蔓延和影响其他服务。5.日志监控:Spring Cloud Gateway提供了丰富的日志功能,可以记录请求和响应的详细信息,帮助开发人员进行故障排查和性能优化。可以将日志输出到控制台、文件或者集成ELK等日志分析工具进行进一步的处理。也可以使用监控工具对网关的性能和流量进行实时监控和统计。总之,Spring Cloud Gateway在构建微服务架构中起到了重要的作用,不仅可以作为反向代理来转发请求,还提供了丰富的功能来增强系统的安全性、稳定性和可观察性。以上介绍的鉴权、限流、熔断和日志监控是其中的核心功能之一,可以根据具体的需求和场景进行灵活配置和扩展。

二、Gateway三大核心
- Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
- Predicate(断言):参考的是Java8的iava.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。
- Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。

web前端请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。predicate就是我们的匹配条件;filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了
三、Gateway工作流程
- 在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;
- 在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
入门配置
- 建Module
- 改POM
- 写YML
- 主启动
- 自业务类
- 测试
建Module
cloud-gateway9527
改POM
<!--gateway 依赖-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.atguigu.cloud</groupId><artifactId>mscloudV5</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>cloud-gateway9527</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!--gateway--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-discovery</artifactId></dependency><!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
写YML
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}
主启动
package com.atguigu.cloud;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@EnableDiscoveryClient //服务注册和发现
public class Main9527
{public static void main(String[] args){SpringApplication.run(Main9527.class,args);}
}
业务类
无,不写任何业务代码,网关和业务无关
测试

- 先启动8500服务中心Consul
- 再启动9527网关入驻
四、9527网关进行路由映射
我们目前不想暴露8001端口希望在8001真正的支付微服务外面套一层9527网关
1.编写支付正常业务逻辑
package com.atguigu.cloud.controller;import cn.hutool.core.util.IdUtil;
import com.atguigu.cloud.entities.Pay;
import com.atguigu.cloud.resp.ResultData;
import com.atguigu.cloud.service.PayService;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.TimeUnit;@RestController
public class PayGateWayController
{@ResourcePayService payService;@GetMapping(value = "/pay/gateway/get/{id}")public ResultData<Pay> getById(@PathVariable("id") Integer id){Pay pay = payService.getById(id);return ResultData.success(pay);}@GetMapping(value = "/pay/gateway/info")public ResultData<String> getGatewayInfo(){return ResultData.success("gateway info test:"+ IdUtil.simpleUUID());}
}
- http://localhost:8001/pay/gateway/get/1
- http://localhost:8001/pay/gateway/info
2.9527网关YML新增配置
cloud-gateway9527 模块添加网关yml配置
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名uri: http://localhost:8001 #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名uri: http://localhost:8001 #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
3.测试1
启动Consul8500服务启动8001支付启动9527网关访问说明目前8001支付微服务前面添加GateWay成功
- http://localhost:8001/pay/gateway/get/1
- http://localhost:8001/pay/gateway/info
- http://localhost:9527/pay/gateway/get/1
- http://localhost:9527/pay/gateway/info


4.测试2
启动订单微服务测试,看看是否通过网关?
我们启动80订单微服务,它从Consul注册中心通过微服务名称找到8001支付微服务进行调用,80 → 9527 → 8001要求访问9527网关后才能访问8001,如果我们此时启动80订单,可以做到吗?

1.修改cloud-api-commons
package com.atguigu.cloud.apis;import com.atguigu.cloud.entities.PayDTO;
import com.atguigu.cloud.resp.ResultData;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;@FeignClient(value = "cloud-payment-service")
public interface PayFeignApi
{/*** GateWay进行网关测试案例01* @param id* @return*/@GetMapping(value = "/pay/gateway/get/{id}")public ResultData getById(@PathVariable("id") Integer id);/*** GateWay进行网关测试案例02* @return*/@GetMapping(value = "/pay/gateway/info")public ResultData<String> getGatewayInfo();}
2.修改cloud-consumer-feign-order88
package com.atguigu.cloud.controller;import com.atguigu.cloud.apis.PayFeignApi;
import com.atguigu.cloud.resp.ResultData;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;@RestController
public class OrderGateWayController
{@Resourceprivate PayFeignApi payFeignApi;@GetMapping(value = "/feign/pay/gateway/get/{id}")public ResultData getById(@PathVariable("id") Integer id){return payFeignApi.getById(id);}@GetMapping(value = "/feign/pay/gateway/info")public ResultData<String> getGatewayInfo(){return payFeignApi.getGatewayInfo();}
}
- http://localhost/feign/pay/gateway/get/1
- http://localhost/feign/pay/gateway/info
- http://localhost/feign/pay/gateway/get/1
- http://localhost/feign/pay/gateway/info
正确做法
同一家公司自己人,系统内环境,直接找微服务
@FeignClient(value = "cloud-payment-service")//自己人内部,自己访问自己,写微服务名字OK
public interface PayFeignApi
{/*** GateWay进行网关测试案例01* @param id* @return*/@GetMapping(value = "/pay/gateway/get/{id}")public ResultData getById(@PathVariable("id") Integer id);/*** GateWay进行网关测试案例02* @return*/@GetMapping(value = "/pay/gateway/info")public ResultData<String> getGatewayInfo();
}
不同家公司有外人,系统外访问,先找网关再服务
//@FeignClient(value = "cloud-payment-service")//自己人内部,自己访问自己,写微服务名字OK
@FeignClient(value = "cloud-gateway")//不同家公司有外人,系统外访问,先找网关再服务
public interface PayFeignApi

1.刷新feign接口jar包
2.重启80订单微服务
3.有网关正常success

4.无网关异常


五、GateWay高级特性
- Route以微服务名-动态获取服务URI
- Predicate浙言(谓词)
- Filter过滤
Route以微服务名-动态获取服务URI

ReactiveLoadBalancerClientFilter(负载均衡和动态路由)
spring:cloud:gateway:routes:- id: myRouteuri: lb://servicepredicates:- Path=/service/**
uri地址修改为动态获取
解决uri写死问题
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名uri: http://localhost:8001 #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名uri: http://localhost:8001 #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
测试1:
测试2:

Predicate断言(谓词)
简介
官网地址: Spring Cloud GatewaySpring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合.Route Predicate Factories(路由谓词工厂)是Spring Cloud Gateway中用于定义路由匹配条件的组件。它们允许您根据请求的不同属性(如路径、请求头、查询参数等)进行路由匹配,从而决定哪些请求应该被转发到特定的服务。
内置断言工厂介绍
- PathRoutePredicateFactory:基于请求路径进行匹配。
- MethodRoutePredicateFactory:基于HTTP请求方法进行匹配。
- HeaderRoutePredicateFactory:基于请求头进行匹配,支持正则表达式。
- QueryRoutePredicateFactory:基于查询参数进行匹配,支持正则表达式。
- CookieRoutePredicateFactory:基于Cookie值进行匹配,支持正则表达式。
- HostRoutePredicateFactory:基于请求的主机名进行匹配。
- AfterRoutePredicateFactory、BeforeRoutePredicateFactory、BetweenRoutePredicateFactory:基于时间进行匹配,允许指定请求应该在某个时间之后、之前或两个时间之间才被路由。
- RemoteAddrRoutePredicateFactory:基于客户端IP地址进行匹配,支持CIDR表示法。

常见的内置Route Predicate使用
- 配置语法总体概述
- 测试地址
- 常用断言api
- 上述配置小总结
Shortcut Configuration 快捷方式

Fully Expanded Arguments 完全展开

#id:我们自定义的路由 ID,保持唯一##uri:目标服务地址##predicates:路由条件,Predicate接受一个输入参数返回一个布尔值。## 该属性包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)
1.After Route Predicate (后路由谓词工厂)
spring:cloud:gateway:routes:- id: after_routeuri: https://example.orgpredicates:- After=2017-01-20T17:42:47.789-07:00[America/Denver]
具体代码实现:yml
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- After=2023-11-20T17:38:13.586918800+08:00[Asia/Shanghai]- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
package com.atguigu.test;
import java.time.ZoneId;
import java.time.ZonedDateTime;public class ZonedDateTimeDemo
{public static void main(String[] args){ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区System.out.println(zbj);}
}
2. Before Route Predicate(前路由谓词工厂)
spring:cloud:gateway:routes:- id: before_routeuri: https://example.orgpredicates:- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
3. Between Route Predicate(中间路由谓词工厂)
spring:cloud:gateway:routes:- id: between_routeuri: https://example.orgpredicates:- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
4. Cookie Route Predicate (Cookie路线谓词工厂)
spring:cloud:gateway:routes:- id: cookie_routeuri: https://example.orgpredicates:- Cookie=chocolate, ch.p
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Cookie=username,zzyy



5. Header Route Predicate(头文件路由谓词工厂)
spring:cloud:gateway:routes:- id: header_routeuri: https://example.orgpredicates:- Header=X-Request-Id, \d+
server:port: 9527
spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式



6. Host Route Predicate (主机路由谓词工厂)
spring:cloud:gateway:routes:- id: host_routeuri: https://example.orgpredicates:- Host=**.somehost.org,**.anotherhost.org
server:port: 9527
spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Host=**.atguigu.com

7.Method Route Predicate(方法路由谓语工厂)
spring:cloud:gateway:routes:- id: method_routeuri: https://example.orgpredicates:- Method=GET,POST
8. Path Route Predicate (路径路由谓词工厂)
spring:cloud:gateway:routes:- id: path_routeuri: https://example.orgpredicates:- Path=/red/{segment},/blue/{segment}
MapuriVariables=ServerWebExchangeUtils.getUriTemplateVariables (exchange);字符串segment=uriVariables.get("segment");
server:port: 9527
spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
9. Query Route Predicate(查询路由谓词工厂)
支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式。
spring:cloud:gateway:routes:- id: query_routeuri: https://example.orgpredicates:- Query=red, gree.
server:port: 9527
spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
10.RemoteAddr route predicate(远程地址路由谓词工厂)
spring:cloud:gateway:routes:- id: remoteaddr_routeuri: https://example.orgpredicates:- RemoteAddr=192.168.1.1/24
server:port: 9527
spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- RemoteAddr=192.168.124.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。

上述配置小总结
Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- After=2023-12-30T23:02:39.079979400+08:00[Asia/Shanghai]#- Cookie=username,zzyy# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式#- Host=**.atguigu.com#- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由#- RemoteAddr=192.168.124.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
自定义断言,XXXRoutePredicateFactory规则
自定义RoutePredicateFactory实现方式

- 要么继承AbstractRoutePredicateFactory抽象类
- 要么实现RoutePredicateFactory接
- 开头任意取名,但是必须以RoutePredicateFactory后缀结尾
自定义路由断言规则步骤套路
编写步骤
1.新建类名XXX需要以RoutePredicateFactory结尾并继承AbstractRoutePredicateFactory类2.重写apply方法3.新建apply方法所需要的静态内部类MyRoutePredicateFactory.Config,这个Config类就是我们的路由断言规则,重要4.空参构造方法,内部调用super5.重写apply方法第二版
@Component标注不可忘
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config>
{
}
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config)
{return null;
}
@Validated
public static class Config{@Setter@Getter@NotEmptyprivate String userType; //钻、金、银等用户等级
}
public MyRoutePredicateFactory()
{super(MyRoutePredicateFactory.Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config)
{return new Predicate<ServerWebExchange>(){@Overridepublic boolean test(ServerWebExchange serverWebExchange){//检查request的参数里面,userType是否为指定的值,符合配置就通过String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");if (userType == null) return false;//如果说参数存在,就和config的数据进行比较if(userType.equals(config.getUserType())) {return true;}return false;}};
}
完整代码
package com.atguigu.cloud.mygateway;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config>
{public MyRoutePredicateFactory(){super(MyRoutePredicateFactory.Config.class);}@Validatedpublic static class Config{@Setter@Getter@NotEmptyprivate String userType; //钻、金、银等用户等级}@Overridepublic Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config){return new Predicate<ServerWebExchange>(){@Overridepublic boolean test(ServerWebExchange serverWebExchange){//检查request的参数里面,userType是否为指定的值,符合配置就通过String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");if (userType == null) return false;//如果说参数存在,就和config的数据进行比较if(userType.equals(config.getUserType())) {return true;}return false;}};}
}
测试1
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- After=2023-12-30T23:02:39.079979400+08:00[Asia/Shanghai]#- Cookie=username,zzyy# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式#- Host=**.atguigu.com#- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由#- RemoteAddr=192.168.124.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。- My=diamond- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由


server:port: 9527
spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- After=2023-12-30T23:02:39.079979400+08:00[Asia/Shanghai]#- Cookie=username,zzyy# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式#- Host=**.atguigu.com#- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由#- RemoteAddr=192.168.124.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。#- My=diamond- name: Myargs:userType: diamond- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由


bug分析

测试2
完整代码02
package com.atguigu.cloud.mygateway;import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config>
{public MyRoutePredicateFactory(){super(MyRoutePredicateFactory.Config.class);}@Validatedpublic static class Config{@Setter@Getter@NotEmptyprivate String userType; //钻、金、银等用户等级}@Overridepublic Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config){return new Predicate<ServerWebExchange>(){@Overridepublic boolean test(ServerWebExchange serverWebExchange){//检查request的参数里面,userType是否为指定的值,符合配置就通过String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");if (userType == null) return false;//如果说参数存在,就和config的数据进行比较if(userType.equals(config.getUserType())) {return true;}return false;}};}@Override
public List<String> shortcutFieldOrder() {return Collections.singletonList("userType");
}}
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- After=2023-12-30T23:02:39.079979400+08:00[Asia/Shanghai]#- Cookie=username,zzyy# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式#- Host=**.atguigu.com#- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由#- RemoteAddr=192.168.124.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。- My=diamond#- name: My# args:# userType: diamond- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
Filter过滤
概述

作用:
- 请求鉴权
- 异常处理
- 记录接口调用时长统计
类型:
Gateway内置的过滤器
1.请求头(RequestHeader)相关组
添加、删除、修改请求头
6.1.The AddRequestHeader GatewayFilter Factory(添加请求头网关过滤器工厂)
- 指定请求头内容ByName
- 8001微服务PayGateWayController新增方法
package com.atguigu.cloud.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.cloud.entities.Pay;
import com.atguigu.cloud.resp.ResultData;
import com.atguigu.cloud.service.PayService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.Enumeration;/*** Gateway网关——路由映射 实例**/
@RestController
public class PayGateWayController {@ResourcePayService payService;@GetMapping(value = "/pay/gateway/filter")public ResultData<String> getGatewayFilter(HttpServletRequest request){String result = "";Enumeration<String> headers = request.getHeaderNames();while(headers.hasMoreElements()){String headName = headers.nextElement();String headValue = request.getHeader(headName);System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);if(headName.equalsIgnoreCase("X-Request-red")|| headName.equalsIgnoreCase("X-Request-blue")) {result = result+headName + "\t " + headValue +" ";}}return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());}
}
3.9527网关YML添加过滤内容
- AddRequestHeader=X-Request-red,redValue # 请求头kv,若一头含有多参则重写一行设置- AddRequestHeader=X-Request-blue,blueValue
spring:cloud: gateway:routes:- id: pay_routh3 #pay_routh3uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由filters:- AddRequestHeader=X-Request-red,redValue # 请求头kv,若一头含有多参则重写一行设置- AddRequestHeader=X-Request-blue,blueValue
4.重启9527和8001并再次调用地址

6.18.The RemoveRequestHeader GatewayFilter Factory(移除请求头网关过滤器工厂)
- 删除请求头ByName
- 修改前

3.YML
- RemoveRequestHeader= sec-fetch-site # 删除请求头sec-fetch-site
spring:cloud:gateway:routes:- id: pay_routh3 #pay_routh3uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由filters:- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
4.重启9527和8001并再次调用地址
5.修改后

6.29.The SetRequestHeader GatewayFilter Factory(设置请求头网关过滤器工厂)
- 修改请求头ByName
- 修改前(sec-fetch-mode)

3.YML参数设置
- SetRequestHeader=sec-fetch-mode, Blue-updatebyzzyy # 将请求头sec-fetch-mode对应的值修改为Blue-updatebyzzyy
4.重启9527和8001并再次调用地址
5.修改后

2.请求参数(RequestParameter)相关组
6.3 The AddRequestParameter GatewayFilter Factory(添加请求参数网关过滤器工厂)
6.19 The RemoveRequestParamter GatewayFilter Factory(移除请求参数网关过滤器工厂)
请求参数示例(6.3/6.19):
- AddRequestParameter=customerId,9527001 #新增请求参数Parameter:k,v- RemoveRequestParameter=customerName #删除url请求参数customerName,设置后传递过来也是null
spring:cloud:gateway:routes:- id: pay_routh3 #pay_routh3uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由filters:- AddRequestParameter=customerId,9527001 #新增请求参数Parameter:k,v- RemoveRequestParameter=customerName #删除url请求参数customerName,设置后传递过来也是null
@GetMapping(value = "/pay/gateway/filter")
public ResultData<String> getGatewayFilter(HttpServletRequest request)
{System.out.println("=============================================");String customerId = request.getParameter("customerId");System.out.println("request Parameter customerId:"+customerId);String customerName = request.getParameter("customerName");System.out.println("request Parameter customerName:"+customerName);System.out.println("=============================================");return ResultData.success("getGatewayFilter 新增/删除请求参数 ");
}
AddRequestParameter=customerId 参数值为设置的参数值:9527001RemoveRequestParameter=customerName 参数值为 null

AddRequestParameter=customerId 参数值为设置的参数值:9999,会根据传递的参数值进行变化RemoveRequestParameter=customerName 参数值为 null————物理是否传参数,参数值都会根据gateway配置设置为null

3.回应头(ResponseHeader)相关组

6.4 The AddResponseHeader GatewayFilter Factory(新增响应头网关过滤器工厂)
AddResponseHeader=X-Response-atguigu, BlueResponse # 新增请求参数X-Response-atguigu并设值为BlueResponse
6.30 The SetResponseHeader GatewayFilter Factory(设置/修改响应头网关过滤器工厂)
SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
6.20 The RemoveResponseHeader GatewayFilter Factory(移除响应头网关过滤器工厂)
RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
请求参数示例(6.4/6.30/6.20):
- AddResponseHeader=X-Response-atguigu, BlueResponse # 新增请求参数X-Response-atguigu并设值为BlueResponse- SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11- RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
spring:cloud:gateway:routes:- id: pay_routh3 #pay_routh3uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由filters:- AddResponseHeader=X-Response-atguigu, BlueResponse # 新增请求参数X-Response-atguigu并设值为BlueResponse- SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11- RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除

4.前缀和路径相关组
6.14.The PrefixPath GatewayFilter Factory(前缀路径网关过滤器工厂)
- Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay- PrefixPath=/pay # http://localhost:9527/pay/gateway/filter
spring:cloud:gateway:routes:- id: prefixpath_routeuri: https://example.orgfilters:- PrefixPath=/mypath

之前完整正确地址: | http://localhost:9527/pay/gateway/filter |
现在完整组合地址: | PrefixPath + Path |
实际调用地址: | http://localhost:9527/gateway/filter 相当于说前缀被过滤器统一管理了。 |

6.29.The SetPath GatewayFilter Factory(设置路径网关过滤器工厂)
- Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代- SetPath=/pay/gateway/{segment} # {segment}表示占位符,你写abc也行但要上下一致
spring:cloud:gateway:routes:- id: pay_routh3 #pay_routh3uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代filters:- SetPath=/pay/gateway/{segment} # {segment}表示占位符,你写abc也行但要上下一致

/XYZ/abc/{segment} | {segment}就是个占位符,等价于SetPath后面指定的{segment}内容 |


6.16.The RedirectTo GatewayFilter Factory(重定向到网关过滤器工厂)
spring:cloud:gateway:routes:- id: pay_routh3 #pay_routh3uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由filters:- RedirectTo=302, http://www.baidu.com/ # 访问http://localhost:9527/pay/gateway/filter跳转到http://www.baidu.com/
5. 其它
6.38. Default Filters(默认全局过滤器)
spring:cloud:gateway:default-filters:- AddResponseHeader=X-Response-Default-Red, Default-Blue- PrefixPath=/httpbin
本次案例全部YML配置全集
server:port: 9527spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- After=2023-12-30T23:02:39.079979400+08:00[Asia/Shanghai]#- Cookie=username,zzyy# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式#- Host=**.atguigu.com#- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由#- RemoteAddr=192.168.124.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。- My=gold
# - name: My
# args:
# userType: diamond- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名#uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由- id: pay_routh3 #pay_routh3uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由,默认正确地址#- Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay#- Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代filters:- RedirectTo=302, http://www.atguigu.com/ # 访问http://localhost:9527/pay/gateway/filter跳转到http://www.atguigu.com/#- SetPath=/pay/gateway/{segment} # {segment}表示占位符,你写abc也行但要上下一致#- PrefixPath=/pay # http://localhost:9527/pay/gateway/filter 被分拆为: PrefixPath + Path#- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置#- AddRequestHeader=X-Request-atguigu2,atguiguValue2#- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site#- SetRequestHeader=sec-fetch-mode, Blue-updatebyzzyy # 将请求头sec-fetch-mode对应的值修改为Blue-updatebyzzyy#- AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v#- RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null#- AddResponseHeader=X-Response-atguigu, BlueResponse # 新增请求参数X-Response-atguigu并设值为BlueResponse#- SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11#- RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
Gateway自定义过滤器
自定义全局Filter
面试题
统计接口调用耗时情况,如何落地,谈谈设计思路答案:通过自定义全局过滤器搞定上述需求
官方介绍
示例
package com.atguigu.cloud.mygateway;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered
{@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){return null;}@Overridepublic int getOrder(){return 0;}
}
predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由predicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由predicates:- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由,默认正确地址
server:port: 9527
spring:application:name: cloud-gateway #以微服务注册进consul或nacos服务列表内cloud:consul: #配置consul地址host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- After=2023-12-30T23:02:39.079979400+08:00[Asia/Shanghai]- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由- id: pay_routh3 #pay_routh3uri: lb://cloud-payment-service #匹配后提供服务的路由地址predicates:- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由,默认正确地址filters:- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
package com.atguigu.cloud.mygateway;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered
{/*** 数字越小优先级越高* @return*/@Overridepublic int getOrder(){return 0;}private static final String BEGIN_VISIT_TIME = "begin_visit_time";//开始访问时间/***第2版,各种统计* @param exchange* @param chain* @return*/@Overridepublic 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");log.info("我是美丽分割线: ###################################################");System.out.println();}}));}}



自定义条件Filter
- SetStatusGatewayFilterFactory
- SetPathGatewayFilterFactory
- AddResponseHeaderGatewayFilterFactory
自定义网关过滤器规则步骤套路
1.新建类名XXX需要以GatewayFilterFactory结尾并继承AbstractGatewayFilterFactory类
@Component //标注不可忘
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
}
2.新建xxXGatewayFilterFactory.config内部类
public static class Config{@Setter@Getterprivate String status;
}
3.重写apply方法
package com.atguigu.cloud.MyGlobalFilter;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
/*** 自定义网关过滤器规则*/
@Component //标注不可忘
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {@Overridepublic GatewayFilter apply(MyGatewayFilterFactory.Config config){return new GatewayFilter(){@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();System.out.println("进入自定义网关过滤器MyGateWayFilter,status==="+config.getStatus());if(request.getQueryParams().containsKey("atuguigu")){return chain.filter(exchange);}else{exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);return exchange.getResponse().setComplete();}}};}public static class Config{@Setter@Getterprivate String status;}
}
4.重写shortcutFieldOrder
@Override
public List<String> shortcutFieldOrder() {List<String> list = new ArrayList<String>();list.add("status");return list;
}
5.空参构造方法,内部调用super
//空参构造方法,内部调用super
public MyGatewayFilterFactory(){super(MyGatewayFilterFactory.Config.class);
}
6.完整代码01
package com.atguigu.cloud.MyGlobalFilter;import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.ArrayList;
import java.util.List;/*** 自定义网关过滤器规则* 参考GateWay内置出厂默认的过滤器,继承 AbstractGatewayFilterFactory类* - SetStatusGatewayFilterFactory* - SetPathGatewayFilterFactory* - AddResponseHeaderGatewayFilterFactory*/
@Component //标注不可忘
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {//空参构造方法,内部调用superpublic MyGatewayFilterFactory(){super(MyGatewayFilterFactory.Config.class);}//重写apply方法 该方法内部重新定义规则@Overridepublic GatewayFilter apply(MyGatewayFilterFactory.Config config){return new GatewayFilter(){@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();System.out.println("进入自定义网关过滤器MyGateWayFilter,status==="+config.getStatus());if(request.getQueryParams().containsKey("atuguigu")){return chain.filter(exchange);}else{exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);return exchange.getResponse().setComplete();}}};}//重写shortcutFieldOrder@Overridepublic List<String> shortcutFieldOrder() {List<String> list = new ArrayList<String>();list.add("status");return list;}public static class Config{@Setter@Getterprivate String status;}
}
YML 参数配置


测试


相关知识拓展:
CIDR使用“IP地址/前缀长度”的格式来表示一个IP地址范围。例如,192.168.1.0/24表示一个IP地址块,其中192.168.1.0是网络的基本地址,/24表示前缀长度为24位,即前24位用于网络标识,剩余的8位用于主机标识。
1.确定IP地址范围:- 首先,需要确定需要划分的IP地址范围。2.确定网络前缀长度:- 根据需要划分的子网数量,确定划分后的网络前缀长度。网络前缀长度可以是任意值,从1到32。前缀长度越长,可分配的子网数量就越多,但每个子网中的主机数量就越少。3.划分子网:- 根据网络前缀长度,将IP地址划分为多个子网。划分时,将IP地址的二进制形式按照网络前缀长度进行分割,前面的部分是网络部分,后面的部分是主机部分。4.分配网络地址:- 为每个子网分配一个唯一的网络地址,通常是网络部分的第一个IP地址。5.分配主机地址:- 根据需要,为每个子网分配一定数量的IP地址,用于分配给主机。6.确定子网掩码:- 子网掩码用于划分网络部分和主机部分。在CIDR中,子网掩码的长度直接由前缀长度决定。
- 假设有一个CIDR地址块192.168.1.0/24,这个地址块包含了从192.168.1.0到192.168.1.255的所有主机。- 如果想要将这个地址块划分为两个子网,可以选择将前缀长度增加1位,即划分为192.168.1.0/25和192.168.1.128/25两个子网。其中,192.168.1.0/25包含了从192.168.1.0到192.168.1.127的地址,而192.168.1.128/25包含了从192.168.1.128到192.168.1.255的地址。
- 灵活性:CIDR允许更灵活地分配IP地址,可以根据实际需求划分不同大小的子网。- 高效性:CIDR减少了路由表的大小,提高了路由效率。通过聚合多个子网到一个CIDR块中,可以减少路由表中的条目数量。- 节省地址空间:CIDR避免了固定类别IP地址分配方案中的地址浪费问题,可以更有效地利用IP地址空间。