赞
踩
前面的内容就不过多的回顾了,可以翻阅一下前面的spring cloud alibaba 完整实现系列,本章我们需要在原有基础上加入gateway网关的使用,以及常见的断言及过滤器设置,至于网关是什么,为什么要使用本章也不会详解,可自行百度,我们暂时还是以搭建为主,文末有源码链接
1.新建一个项目,并加入启动类及yml文件
2.引入gateway的依赖(因为在父pom中引入了spring cloud的版本,所以此次不需要指定具体版本,版本问题可以参考第一篇,注意gateway是属于spring cloud 而不是alibaba)
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-gateway</artifactId>
- </dependency>
3.现在可以直接启动gateway项目了
报错了,当然下面错误也很明显,因为网关是不需要web环境的,所以不能依赖spring boot,前面我们在父pom中直接引入了spring boot的依赖
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
现在使用gateway就需要把这个依赖放到各个需要的子目录中,删除父pom中的依赖,如下图
此时启动,完成,那么gateway集成就完成了,简单伐。歪歪歪?就没了?发现这个玩意集成了也没啥用处呀。不着急,我们一步步的来
现在我们来修改yml文件,刚才没有贴yml,我只是在yml中写了当前的端口,以及服务名
但是现在网关没办法使用,网关的作用是将客户端发送的请求进行转发到对应服务器,我现在有一个用户和日志模块,那他怎么知道要转发到那一台?
我们先来尝试将服务转发到用户模块(静态路由)
- server:
- port: 8880 #自定义端口
-
- spring:
- application:
- name: api-gateway
- cloud:
- gateway:
- routes:
- - id: user-service #唯一标识,建议配合服务名
- uri: http://localhost:8881 #匹配后提供的路由地址
- predicates:
- - Path=/user/** #断言,路径相匹配的进行路由
如果是user/**所有请求,转发到8881下
这个就是网关的简单使用,但是这个会有问题,首先我们是把端口写死的,而且只能匹配user/路径,如果有其他的还得加,我还有日志服务,或者还有其他服务,这个配置不就越写越多了。。。看来生产用这种并不合理
要在变动的服务中间找到对应的内容,最好的方式就是我们的nacos,服务注册了,我们只需要找到对应的注册服务,端口啥的都不用管了:
- cloud:
- gateway:
- routes:
- - id: user-service
- uri: lb://user-service #使用nacos本地负载均衡策略
- #断言规则
- predicates:
- - Path=/**
- nacos:
- discovery:
- server-addr: 127.0.0.1:8848 #本地nacos地址
重启再次访问,也能够搞定,通过nacos中注册的user-service服务名,也能转发到。那么log服务咋搞?现在我们也需要给log服务做一个路由转发,因为我现在是匹配得所有请求,如果后面我还有一个什么服务,两个都有一个userController而且两个路径都是user/怎么办呢?
所以我们的yml还不行
- server:
- port: 8880 #自定义端口
-
- spring:
- application:
- name: api-gateway
- # cloud:
- # gateway:
- # routes:
- # - id: user-service #唯一标识,建议配合服务名
- # uri: http://localhost:8881 #匹配后提供的路由地址
- # predicates:
- # - Path=/user/** #断言,路径相匹配的进行路由
- cloud:
- gateway:
- routes:
- - id: user-service
- uri: lb://user-service #使用nacos本地负载均衡策略
- #断言规则
- predicates:
- - Path=/user-service/**
- filters:
- - StripPrefix=1 #去除上层路径
-
- - id: log-service
- uri: lb://log-service #使用nacos本地负载均衡策略
- #断言规则
- predicates:
- - Path=/log-service/**
- filters:
- - StripPrefix=1
- nacos:
- discovery:
- server-addr: 127.0.0.1:8848 #本地nacos地址

那么两个服务各自转发到各自的服务上这样就可以解决刚才的问题,当然还可以直接开启注册中心路由功能,这样下面的id什么的都可以不用配置了,两种都可以,在实际使用时,上面这种还更多一点,毕竟相对灵活,断言规则及过滤器根好控制下面是自动注册中心的(根据需求自行选择):
- server:
- port: 8880 #自定义端口
-
- spring:
- application:
- name: api-gateway
- # cloud:
- # gateway:
- # routes:
- # - id: user-service #唯一标识,建议配合服务名
- # uri: http://localhost:8881 #匹配后提供的路由地址
- # predicates:
- # - Path=/user/** #断言,路径相匹配的进行路由
- cloud:
- gateway:
- discovery:
- locator:
- enabled: true #开启注册中心路由
- # routes:
- # - id: user-service
- # uri: lb://user-service #使用nacos本地负载均衡策略
- # #断言规则
- # predicates:
- # - Path=/user-service/**
- # filters:
- # - StripPrefix=1 #去除上层路径
- #
- # - id: log-service
- # uri: lb://log-service #使用nacos本地负载均衡策略
- # #断言规则
- # predicates:
- # - Path=/log-service/**
- # filters:
- # - StripPrefix=1
- nacos:
- discovery:
- server-addr: 127.0.0.1:8848 #本地nacos地址

至此,我们集成gateway就完成了,现在我们各个服务的请求都可以从网关进入进行分发。下面我们来看一下关于断言及过滤器
我们先要明白什么是断言,gateway中断言的作用
Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由。
断言就是说: 在 什么条件下 才能进行路由转发 gateway有很多内置的断言工厂:
1. 基于请求时间的断言
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间之内
-After=2022-01-24T23:59:59.789+08:00[Asia/Shanghai]
2. ip地址断言
-RemoteAddr=192.168.1.1/24
3. cookie 断言
cookie是否具有给定名称且值与正则表达式匹配。
-Cookie=name, 正则
4. header 断言
是否具有给定名称且值与正则表达式匹配。
-Header=X-Request-Id, \d+
5. host 断言
主机名模式。判断请求的Host是否满足匹配规则。
-Host=**.baidu.com
6. Method 断言
-Method=GET
7. path 断言(上面我们就有使用这种断言方式)
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
-Path=/foo/{segment} 基于Query请求参数的断言工厂
QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具
有给定名称且值与正则表达式匹配。
-Query=name, 正则
8. 权重断言
接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发(同一服务多个配置才会使用)
-Weight= group1, 1
其实自带的断言工厂就够我们使用了,当然也可以自定义断言工厂我这儿写了两个自定义的断言工厂,代码其实是很简单,调试搞清楚数据就明白如何去调整自己想要的效果了
- package com.andy.gateway.route;
-
- import lombok.Data;
- import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
- import org.springframework.stereotype.Component;
- import org.springframework.util.MultiValueMap;
- import org.springframework.web.server.ServerWebExchange;
-
- import java.util.Arrays;
- import java.util.List;
- import java.util.function.Predicate;
-
- /**
- * 自定义断言规则工厂
- * 实现步骤:
- * 1.创建java类,类名必须以RoutePredicateFactory结尾
- * 2.继承AbstractRoutePredicateFactory 类
- * 3.编写Config内部类,构建断言参数
- * 4.泛型调整为Config
- * 5.重写apply(断言规则逻辑代码) 及 shortcutFieldOrder(断言参数及顺序传入)
- * 6.构造方法调用super传入Config
- * 7.修改yml
- * #断言规则
- * predicates:
- * - name: Custom
- *
- * 这个代码没必要去记,我们还可以找到 AbstractRoutePredicateFactory 的实现类,随便找个复制一下,修改使用也可以
- */
- @Component
- public class CustomRoutePredicateFactory extends AbstractRoutePredicateFactory<CustomRoutePredicateFactory.Config> {
-
- public CustomRoutePredicateFactory(){
- super(Config.class);
- }
-
- @Override
- public Predicate<ServerWebExchange> apply(Config config) {
- return (exchange ->{
- MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
- int sex = Integer.parseInt(queryParams.getFirst("sex"));
- int age = Integer.parseInt(queryParams.getFirst("age"));
- //具体业务判断,false访问失败 true 继续
- if(sex == 0 && age>18 && age<28){
- System.out.println("美女请进");
- return true;
- }
- System.out.println(sex+"---"+config);
- return false;
- });
- }
-
- /**
- * 传入参数字段,及顺序
- * @return
- */
- @Override
- public List<String> shortcutFieldOrder() {
- //参数名称及顺序
- return Arrays.asList("sex","age");
- }
-
- @Data
- static class Config{
- private int sex;
- private int age;
- }
- }

- package com.andy.gateway.route;
-
- import lombok.Data;
- import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
- import org.springframework.http.HttpHeaders;
- import org.springframework.stereotype.Component;
- import org.springframework.web.server.ServerWebExchange;
-
- import java.util.Arrays;
- import java.util.List;
- import java.util.function.Predicate;
-
- /**
- * 自定义 Gateway 断言 Auth 后面必须为 RoutePredicateFactory
- */
- @Component
- public class AuthRoutePredicateFactory extends AbstractRoutePredicateFactory<AuthRoutePredicateFactory.Config> {
-
- public static final String AUTHO_KEY = "name";
- public static final String AUTHO_VALUE = "value";
- public AuthRoutePredicateFactory() {
- super(Config.class);
- }
-
- /**
- * 表示配置填写的顺序,例如:- Auth=zhangsan,xxx, zhangsan 代表 AUTHO_KEY , xxx 代表 AUTHO_VALUE
- * @return
- */
- @Override
- public List<String> shortcutFieldOrder() {
- return Arrays.asList(AUTHO_KEY,AUTHO_VALUE);
- }
-
- @Override
- public Predicate<ServerWebExchange> apply(Config config) {
- // 如果 Header 中携带了某个值,进行 header 的判断
- return (exchange -> {
- // 获取请求 header
- HttpHeaders httpHeaders = exchange.getRequest().getHeaders();
- // 获取指定 header
- List<String> headerList = httpHeaders.get(config.getName());
- //header中是否包含zhangsan 参数,值为xxx
- // if(headerList.contains(config.value)){
- return true;
- // }
- // return false;
- });
- }
-
- /**
- * 获取的是yml里面的值
- */
- @Data
- public static class Config{
- private String name;
- private String value;
- }
- }

这上面是两个自定义断言,第一个实现的是请求参数必须带sex 和 age,然后必须是女,18-28才进行路由转发 。第二个是找请求头中必须包含 参数 zhangsan 而且值为xxx进行转发,yml的改动如下:
- predicates:
- - Path=/user-service/**
- - Auth=zhangsan,xxx
- # - name: Custom 启用Custom自定义断言
作用:过滤器就是在请求的传递过程中,对请求和响应做一些手脚
先看内置的(懒得去统计了,贴一个别人写的吧,需要使用的就对照一下):
实现一个自定义的:
- package com.andy.gateway.filter;
-
- import org.springframework.cloud.gateway.filter.GatewayFilterChain;
- import org.springframework.cloud.gateway.filter.GlobalFilter;
- import org.springframework.core.Ordered;
- import org.springframework.http.HttpStatus;
- import org.springframework.stereotype.Component;
- import org.springframework.web.server.ServerWebExchange;
- import reactor.core.publisher.Mono;
-
- /**
- * 自定义一个全局过滤器
- * 实现 globalfilter , ordered接口
- */
- @Component
- public class LoginFilter implements GlobalFilter, Ordered {
-
- /**
- * 执行过滤器中的业务逻辑
- * 对请求参数中的token进行判断
- * 如果存在此参数:代表已经认证成功
- * 如果不存在此参数 : 认证失败.
- * ServerWebExchange : 相当于请求和响应的上下文(zuul中的RequestContext)
- */
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- System.out.println("执行了自定义的全局过滤器");
- //1.获取请求参数token
- String token = exchange.getRequest().getQueryParams().getFirst("token");
- //2.判断是否存在
- // if(token == null) {
- // //3.如果不存在 : 认证失败
- // System.out.println("没有登录");
- // exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
- // return exchange.getResponse().setComplete(); //请求结束
- // }
- //4.如果存在,继续执行
- return chain.filter(exchange); //继续向下执行
- }
-
- /**
- * 指定过滤器的执行顺序 , 返回值越小,执行优先级越高
- */
- @Override
- public int getOrder() {
- return 0;
- }
- }

我把关键代码注释了,可以做参考,放开后是需要传入token就认为请求成功
其实断言和过滤器有点像,而且有些功能可以说两个都能实现。断言更像是门槛,过滤器则可以在请求中间做一些其他的事情
最后贴一下源码:
链接: https://pan.baidu.com/s/1rMR8OQvAHcNYYqOmnC1naA?pwd=id6h
提取码: id6h
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。