gateway-网关
- 网关
- [spring cloud gateway](#spring cloud gateway)
网关
在微服务架构中一个系统会被拆分成多个微服务。那么作为客户端(前端)要如何去调用这么多的微服务?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去用。
这样的架构,会存在诸多的问题:
每个业务都会需要鉴权、限流、权限校验、跨域等逻辑,如果每个业务都各自为站。自己造轮子实现一遍,完全没必要,完全可以抽离出来,放到一个统一的地方去做
。
上面的问题可以借助网关来解决。
网关:所谓API网关,就是指系统的同一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务功能本身无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等待
添加上API网关之后,系统变成三层结构,系统的架构图如下:
加入网关之后的架构图
spring cloud gateway
- 功能特征
- 基于spring5.0和springboot2.0进行构建
- 动态路由:可以匹配任何请求属性
- 支持路径重写
- 集成服务发现(nacos、ereka)
- 可集成流控降级功能(sentinel、hystrix)
- 可以对路由指定易于编写的断言和过滤器
- 路由
路由是网关中最基础的部分,路由信息包括一个ID、一个目的URL、一组断言工厂、一组filter组成。如果断言为真,则说明请求的RUL和配置的路由匹配;- 断言
Java8中的断言函数,gateway中的断言函数类型是spring5.0框架中的servletWebExchange,断言函数允许开发者去定义匹配http request中的任何信息,比如请求头和参数等。- 过滤器
gateway中的filter分为Gateway Filter和Global Filter,filter可以对请求和响应处理
- 工作原理
spring cloud gateway的工作原理和zuul的差不多,最大的区别就是gateway的filter只有pre和post两种。
gateway初体验
添加依赖
xml
<!--添加springcloud gateway依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
- 配置文件
yaml
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order-server/**
# http://localhost:8090/order-server/order/buyProduct 路由到↓
# http://localhost:8081/order-server/order/buyProduct
filters:
- StripPrefix=1 #内置过滤器,转发之前去掉第一层路径
# http://localhost:8081/order-server/order/buyProduct 去掉后 ↓
# http://localhost:8081/order/buyProduct
gateway整合nacos
你这个是springcloud gateway吧,我跟你说一下为什么这么写配置,是这样的,lb代表了负载均衡,有这个标志后,gateway就知道需要进行负载均衡,转发到lb://后面跟的服务名对应的某台服务器上,至于如何实现负载均衡,是通过负载均衡组件来的,比如ribbon或者springcloud-loadbalancer组件拿到服务列表,从而进行服务的转发。所以lb://,其实跟服务注册中心没有关系,就算不是nacos,用其它的,比如eureka,也是这么写lb://。
yaml
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: lb://order-service #需要转发的地址 lb:使用nacos中的本地负载均衡策略
#断言规则 用于路由规则的匹配
predicates:
- Path=/order-server/**
# http://localhost:8090/order-server/order/buyProduct 路由到↓
# http://localhost:8081/order-server/order/buyProduct
filters:
- StripPrefix=1 #内置过滤器,转发之前去掉第一层路径
# http://localhost:8081/order-server/order/buyProduct 去掉后 ↓
# http://localhost:8081/order/buyProduct
简写方式
当服务注册到nacos后,断言规则可以用服务名来进行匹配,此时配置可以进行简化(约定服务名和断言规则都从nacos获取)
yaml
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
discovery:
locator:
enabled: true #是否启动自动识别nacos服务(约定大于配置)
- 测试
此时访问:http://localhost:8090/order-service/order/buyProduct
内置路由断言工厂
作用:当请求gateway的时候,使用断言对请求进行匹配,如果匹配成功就进行路由转发,如果匹配失败就返回404
内置断言工厂
- 基于Datetime类型的断言工厂
此类型的断言根据时间做判断,主要有三个:
- AfterRoutePredicateFactory:接收一个日期参数,判断请求日期是否晚于指定日期
- BeforeRoutePredicateFactory:接收一个日期参数,判断请求日期是否早于指定日期
- BetweenRoutePredicateFactory:接收一个日期参数,判断请求日是否在指定时间段内
类型:zoneDateTime.now():System.out.println(ZonedDateTime.now());
-After=2024-12-31T23:59:59.789+08:00[Asia/Shanghai]
yaml
#第一版
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order/**
- After=2024-12-31T23:59:59.789+08:00[Asia/Shanghai]
- 基于请求头Headder的断言工厂
- HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式,判断请求Header是否具有给定名称且值与正则表达式匹配。
- Header=X-Request-Id,\d+
yaml
#第一版
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order/**
- After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai]
- Header=X-Request-Id,\d+
测试
- 基于Method请求方法的断言工厂
- MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配
- Method=GET
yaml
#第一版
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order/**
- After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai]
- Header=X-Request-Id,\d+
- Method=GET
- 基于Query请求参数的断言工厂
- QueryRoutePredicateFactory:接收两个参数,请求param和正则表达式,判断请求参数是否具有给定名称且值与正则表达式匹配。
- Query=id,\d+
yaml
#第一版
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order/**
- After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai]
- Header=X-Request-Id,\d+
- Method=GET
- Query=id,\d+
测试
自定义路由断言工厂
自定义路由断言工厂需要继承AbstractRoutePredicateFactory类 ,重写apply方法的逻辑。在apply方法中可以通过exchange.getRequest()拿到serverletRequest对象,从而可以获取到请求的参数、请求方式、请求头等信息。
1、必须是bean组件
2、由于约定大于配置:类名必须加上RoutePredicateFactory作为结尾
3、必须继承AbstractRoutePredicateFactory类
4、必须声明一个静态内部类,声明属性来接收配置文件中对应的信息
5、需要结合shortcutFieldOrder来进行绑定
6、通过apply进行逻辑判断,true就是匹配成功,false匹配失败
自定义路由工厂
-checkAuth=zzq,如果配置文件中checkAuth=zzq,则匹配通过,否则匹配失败
- 配置文件
yaml
#第一版
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order/**
- After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai]
- Header=X-Request-Id,\d+
- Method=GET
- Query=id,\d+
- CheckAuth=zzq
- 自定义代码
java
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {
public CheckAuthRoutePredicateFactory() {
super(CheckAuthRoutePredicateFactory.Config.class);
}
/**
* 绑定配置信息
* @return
*/
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("name");
}
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
if (config.getName().equals("zzq")){
return true;
}
return false;
}
};
}
/**
* 用于接收配置文件中 断言的信息
*/
@Validated
@Data
public static class Config {
private String name;
}
}
- 测试
- 修改配置后
yaml
#第一版
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order/**
- After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai]
- Header=X-Request-Id,\d+
- Method=GET
- Query=id,\d+
- CheckAuth=qwee
- 测试
内置/自定义过滤器
gateway内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,添加去除参数等。
典型内置过滤器
- 添加请求头
- AddRequestHeader=X-Request-color,red #添加请求头
yaml
#第一版
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order/**
- After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai]
- Header=X-Request-Id,\d+
- Method=GET
- Query=id,\d+
- CheckAuth=zzq
filters:
- AddRequestHeader=X-Request-color,red #添加请求头
- 为匹配的路由添加统一前缀
- PrefixPath=/shanghaiserver #添加前缀 对应微服务需要配置context-path
- order.yml配置context-path
yaml
server:
port: 8081
servlet:
context-path: /shanghaiserver
- gateway为了适配添加前缀
yaml
#第一版
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order/**
- After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai]
- Header=X-Request-Id,\d+
- Method=GET
- Query=id,\d+
- CheckAuth=zzq
filters:
- AddRequestHeader=X-Request-color,red #添加请求头
- PrefixPath=/shanghaiserver #添加前缀 对应微服务需要配置context-path
自定义过滤器
继承AbstractGatewayFilterFactory且自定义类的名称必须以GatewayFilterFactory结尾,且必须交给spring进行管理
- 自定义过滤器
java
package com.springcloudalibaba.gateway.config;
import com.alibaba.nacos.api.utils.StringUtils;
import lombok.Data;
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.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
/**
* @author : zhouzhiqiang
* @date : 2024/6/19 12:51
* @description :
*/
@Component
public class CheckParamGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckParamGatewayFilterFactory.Config> {
public CheckParamGatewayFilterFactory() {
super(CheckParamGatewayFilterFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("name");
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String name = exchange.getRequest().getQueryParams().getFirst("name");
if (!StringUtils.isBlank(name)) {
//如果相等请求被放过
if (config.getName().equals(name)) {
return chain.filter(exchange);
} else {
//如果不相等返回404
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
return exchange.getResponse().setComplete();
}
}
return chain.filter(exchange);
}
};
}
/**
* 用于接收配置文件中 过滤器的信息
*/
@Validated
@Data
public static class Config {
private String name;
}
}
- 配置自定义的过滤器工厂
yaml
#第一版
server:
port: 8090
# gateway配置
spring:
application:
name: api-gateway
cloud:
#nacos配置
nacos:
discovery:
server-addr: 192.168.184.15:8848
username: nacos
password: nacos
#gateway配置
gateway:
#路由规则
routes:
- id: order_router #路由的唯一标识,路由到order
uri: http://localhost:8081 #需要转发的地址
#断言规则 用于路由规则的匹配
predicates:
- Path=/order/**
- After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai]
- Header=X-Request-Id,\d+
- Method=GET
- Query=id,\d+
- CheckAuth=zzq
filters:
- AddRequestHeader=X-Request-color,red #添加请求头
- PrefixPath=/shanghaiserver #添加前缀 对应微服务需要配置context-path
- CheckParam=zzq
- 测试
- 成功
- 失败
全局过滤器
对比局部过滤器,全局过滤器会对所有的路由请求进行过滤