spring cloud alibaba 完整实现(四)gateway网关集成
前面的内容就不过多的回顾了,可以翻阅一下前面的spring cloud alibaba 完整实现系列,本章我们需要在原有基础上加入gateway网关的使用,以及常见的断言及过滤器设置,至于网关是什么,为什么要使用本章也不会详解,可自行百度,我们暂时还是以搭建为主,文末有源码链接1.新建一个项目,并加入启动类及yml文件2.引入gateway的依赖(因为在父pom中引入了spring cloud的版
前面的内容就不过多的回顾了,可以翻阅一下前面的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
更多推荐
所有评论(0)