目录
1、首先来介绍一下gateway:Gateway(网关)网络的关口。
接着上篇:
之前写了,使用nacos的注册和配置、OpenFeign的远程调用;这篇文章是关于Gateway的使用,通过这篇文章,你能在微服务中使用gateway网关,并能做一些简单的配置;
nacso: springboot3整合nacos实现注册中心和配置中心(详细入门)-CSDN博客
Openfeign: spring boot整合openfeign实现两个微服务之间的调用。-CSDN博客
gateway中文网:Spring Cloud Gateway 中文文档
1、首先来介绍一下gateway:
Gateway(网关)网络的关口。
是一种用于构建微服务架构中的统一访问入口的服务器。它充当了客户端和后端微服务之间的中介,负责请求的路由、转发、身份校验、过滤、转换和聚合等功能。
网关与各个微服务之间的关系:
网关的组成:通过路由(Route)、过滤器(Filter)和断言(Predicate)组成了一个完整的网关服务
gatewry
路由、谓词与过滤器组成
- 路由:id,uri
谓词:-Path、-Header -After 等等条件组成
过滤器:filter
gatewry常与nacos注册中心,loadBalancer负载均衡等一起使用
在之前项目的基础上,我们再来添加网关应用;
一个父项目,下面有三个子模块。父项目中进行依赖的管理和维护,有一个生产者模块(producer)、一个消费者模块(consumer),还有一个OpenFeign模块(api),专门用来进行微服务之间的调用。
2、再之前项目的基础上添加一个新模块(gateway):
并在这个模块中新引入一些依赖
<dependencies> <!-- gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!-- nacos注册中心--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--loadBalancer负载均衡--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> </dependencies>
注意:引入了gateway依赖之后,就不用再引入web依赖了。不然两个依赖有冲突会报错的
主要原因是Spring Boot的自动装配机制。
Spring Cloud Gateway依赖包含了Spring WebFlux,它是基于响应式编程模型构建的非阻塞Web框架。而传统的Spring Web依赖(spring-boot-starter-web)是基于Servlet容器的阻塞式Web框架。
当同时引入Spring Cloud Gateway和spring-boot-starter-web依赖时,两者之间可能存在冲突,因为它们使用不同的Web框架和Servlet容器。
解决这个问题的方法是选择其中一种依赖,根据项目需求和开发模式进行选择:
-
如果你想使用Spring Cloud Gateway,那么可以移除spring-boot-starter-web依赖,只保留Spring Cloud Gateway相关的依赖。这样可以完全采用非阻塞的WebFlux编程模型。
-
如果你需要使用传统的Spring MVC编程模型,可以移除Spring Cloud Gateway的依赖,只保留spring-boot-starter-web依赖。这样可以使用基于Servlet容器的阻塞式Web框架。
需要注意的是,这两种方式是互斥的,不能同时使用。根据项目需求和开发模式选择适合的依赖,并确保版本兼容性,可以避免相关错误的发生。
3、在gateway模块中配置初始属性:
1、创建启动类
2、在yml文件中设置nacos的注册信息和gateway网关的路由:
server: port: 8080 spring: application: name: gateway cloud: nacos: discovery: server-addr: 192.168.231.110:8848 gateway: routes: - id: consumer #路由规则id,自定义,唯一 uri: lb://consumer #路由目标的微服务,lb代表负载均衡 predicates: #路由断言,判断请求是否符合规则,符合则路由到目标 - Path= /consumer/** #以请求路径做判断,以/user开头的符合 - id: producer uri: lb://producer predicates: - Path= /producer/**
先将gateway添加到nacos注册中心中,然后解释一下路由(routes)的一些参数
-
id:每个路由规则都需要一个唯一的
id
来标识,它可以是任意字符串。id
主要用于在日志和监控中标识特定的路由。 -
uri:
uri
表示目标服务的统一资源标识符(Uniform Resource Identifier),可以是HTTP、HTTPS或自定义协议等。你可以使用http://
或https://
开头指定目标服务的地址,并且可以包含相关的路径、查询参数、片段等信息。这里我们将gateway模块也放在了nacos中。因此这里可以直接写模块在nacos中的服务名称 -
predicates:
predicates
用于匹配请求的条件,只有满足条件的请求才会被路由到对应的目标服务。你可以根据请求的URI、方法、头部等属性来定义条件,并使用逻辑运算符(如AND、OR、NOT)组合多个条件。在这里我们规定根据请求头来将相应的请求转发到特定的微服务;
4、将这三个模块都启动起来,然后来访问gateway模块的端口,看能不能转发到相应的模块:
请求producer模块的方法:发现我们访问的是8080gateway模块的端口,却能映射到producer模块
请求consumer 模块的方法:
Spring Cloud Gateway提供了12个基本的路由断言,用于匹配请求并将其路由到相应的微服务实例。这些基本的路由断言如下:
After: 匹配请求日期时间在指定时间之后的条件,例如:after=2022-01-01T00:00:00.000+08:00[Asia/Shanghai]。
Before: 匹配请求日期时间在指定时间之前的条件,例如:before=2022-01-01T00:00:00.000+08:00[Asia/Shanghai]。
Between: 匹配请求日期时间在两个指定时间之间的条件,例如:between=2022-01-01T00:00:00.000+08:00[Asia/Shanghai], 2022-12-31T23:59:59.999+08:00[Asia/Shanghai]。
Cookie: 匹配请求中携带指定Cookie的条件,例如:Cookie=name, value。
Header: 匹配请求中包含指定Header的条件,例如:Header=X-Request-Id, \d+。
Host: 匹配请求的Host头部信息的条件,例如:Host=**.example.com。
Method: 匹配请求的HTTP方法的条件,例如:Method=GET。
Path: 匹配请求的路径的条件,例如:Path=/foo/{segment}。
Query: 匹配请求的查询参数的条件,例如:Query=foo, ba?。
RemoteAddr: 匹配请求的远程地址的条件,例如:RemoteAddr=192.168.1.1/24。
Weight: 通过权重来匹配多个微服务实例的条件,例如:Weight=group1=4,group2=6。
XForwader Remote Addr: 基于请求的来源Ip做判断, 例如:-XForwaderRemoteAddr=192.168.231.110/80
相应的图片:
这些我之前使用了Path路径匹配。剩下的我就不一一演示了,感兴趣的可以自己演示;
4、网关(gateway)过滤器:
Spring Cloud Gateway的过滤器提供了一种强大的机制来对请求进行修改和处理。通过合理配置和编写自定义的过滤器,你可以实现各种功能,如鉴权、请求日志记录、限流等
Spring Cloud Gateway中的过滤器分为两种类型:全局过滤器和局部过滤器。
1、全局过滤器:全局过滤器会应用于所有的路由规则。你可以使用GlobalFilter
接口实现全局过滤器,该接口定义了一个名为filter
的方法,用于对请求进行处理。全局过滤器的顺序可以通过实现Ordered
接口来进行控制
有两种实现方式:
(1)、简单的在yml配置文件种进行实现:
如:我想在每个请求到来时,都在请求头上加入一个参数;
名字叫name,值为张三(name=张三)
gateway: routes: - id: consumer-test #路由规则id,自定义,唯一 uri: lb://consumer #路由目标的微服务,lb代表负载均衡 predicates: #路由断言,判断请求是否符合规则,符合则路由到目标 - Path= /consumer/** #以请求路径做判断,以/user开头的符合 - id: producer-test uri: lb://producer predicates: - Path= /producer/** default-filters: #全局过滤器 - AddRequestHeader=name,zhangsan
在服务中进行接收,看能不能接收到我们田间的参数(name=zhangsan)
在consumer模块中接收name属性:
@GetMapping("/test") public String test(@RequestHeader(value = "name" ,required = false) String name){ System.out.println("name====>" +name); System.out.println("来自服务提供者的数据"); return "consumer服务的test方法"; }
访问consumer/test方法,看能不能接收到name属性:
consumer模块结果:可以看到我们并没有在请求头种设置name属性,但是却接收到了name参数,这说明我们的过滤器生效了
(注意这种方式能传递数字,英文。但是不能传递中文,解析不了会出现乱码)
(2)、实现GlobalFilter接口的filter方法:
@Component //@Order(0) //使用注解,或者实现Ordered接口 public class MyGlobalFilter implements GlobalFilter , Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获取请求 ServerHttpRequest request = exchange.getRequest(); URI uri = request.getURI(); System.out.println("请求的uri=========>"+uri); //过滤器的业务处理 System.out.println("执行了业务处理"); // 放行 return chain.filter(exchange); } @Override public int getOrder() { // 过滤器执行顺序,数值越小,优先级越高 return 0; } }
执行的结果如下:可以看到执行了我们在全局过滤器种定义的逻辑:
2、局部过滤器:局部过滤器仅应用于特定的路由规则。你可以使用GatewayFilterFactory
接口和它的子类来实现局部过滤器
(1)、简单的在yml配置文件种进行实现:
gateway: routes: - id: consumer-test #路由规则id,自定义,唯一 uri: lb://consumer #路由目标的微服务,lb代表负载均衡 predicates: #路由断言,判断请求是否符合规则,符合则路由到目标 - Path= /consumer/** #以请求路径做判断,以/user开头的符合 - id: producer-test uri: lb://producer predicates: - Path= /producer/** filters: - AddRequestHeader=name,zhangsan
这里的结果我就不再截图了,局部过滤器的生效范围只能在特定的路由;
(2)继承AbstractGatewayFilterFactory类,并重写GatewayFilter方法:
// 固定的GatewayFilterFactory类名后缀,方便配置使用 // 局部的过滤器配置完不能立即生效,还要再yml配置文件种添加filters过滤参数 // 这个参数为ProducerGatewayFilterFactory类名的前一部分:Producer @Component public class ProducerGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> { @Override public GatewayFilter apply(Object config) { return new OrderedGatewayFilter(new GatewayFilter() { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); URI uri = request.getURI(); System.out.println(" 路径为"+uri); System.out.println(" Producer模块的局部过滤器"); return chain.filter(exchange); } }, 2); } }
局部过滤器的实现方法比全局过滤器要复杂的多,我们这个类名也不能随便起名,
前一部分一般是模块的名称如(Producer),后部分固定为GatewayFilterFactory。
并且重写GatewayFilter方法之后,要返回的是一个过滤器。我们这里使用了匿名内部类的形式,重写了filter方法,这个方法里面才是集体的过滤器逻辑;
兵器这边写完之后,还不算完。我们还要再yml配置文件中指定相应的过滤器的名称:
gateway: routes: - id: consumer-test #路由规则id,自定义,唯一 uri: lb://consumer #路由目标的微服务,lb代表负载均衡 predicates: #路由断言,判断请求是否符合规则,符合则路由到目标 - Path= /consumer/** #以请求路径做判断,以/user开头的符合 - id: producer-test uri: lb://producer predicates: - Path= /producer/** filters: - Producer #这里一定要写上,不然我们自定义的过局部滤器类是不会生效的
请求Producer模块的test方法:
gateway模块的输出结果如下:
看到这个,说明我们的全局过滤器和Producer模块的局部过滤器都实现了。
当全局过滤器和局部过滤器同时存在时,会按照它们是实现的Ordered()方法的数值大小来决定谁先实现。
一般请求下我们都使用全局过滤器,很少使用局部的过滤器。因为gateway网关的作用一个管家,帮我们把相应的请求路由到具体模块,最多最多也就是设置一个全局过滤器做一些通用的拦截(一般也就是用户的校验)这就顶天了,不会在做其他复杂的操作;
在Spring Cloud Gateway的配置文件中,有几个常用的过滤器可以使用。以下是其中一些常见的过滤器及其用途:
1. `AddRequestHeader`:用于向请求头中添加指定的头信息。
filters:
- AddRequestHeader=X-ExampleHeader, ExampleValue
2. `RemoveRequestHeader`:用于从请求头中移除指定的头信息。
filters:
- RemoveRequestHeader=X-UnwantedHeader
3. `AddResponseHeader`:用于向响应头中添加指定的头信息。
filters:
- AddResponseHeader=X-ResponseHeader, ResponseValue
4. `RemoveResponseHeader`:用于从响应头中移除指定的头信息。
filters:
- RemoveResponseHeader=X-UnwantedResponseHeader
5. `RewritePath`:用于重写请求路径,可以替换或删除部分路径。
filters:
- RewritePath=/oldPath/(?<segment>.*), /newPath/$\{segment}
6. `SetPath`:用于设置请求的路径。
filters:
- SetPath=/newPath
7. `SetRequestHeader`:用于设置请求头的值。
filters:
- SetRequestHeader=X-CustomHeader, CustomValue
8. `SetResponseHeader`:用于设置响应头的值。
filters:
- SetResponseHeader=X-CustomResponseHeader, CustomResponseValue
这些是一些常用的过滤器示例,你可以根据具体的项目需求选择适合的过滤器并在配置文件中进行配置。