目录
五、路由断言工厂(Route Predicate Factories)
六、过滤器工厂(Gateway Filter Factories)
一、前言
所谓API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,比如:认证、鉴权、监控、路由转发等。
利用API网关可以解决以下问题:
- 全局性流控
- 日志统计
- 防止sql注入
- 防止web攻击
- 屏蔽工具扫描
- 黑白ip名单
- 证书/加解密处理
- 服务级别流控
- 服务降级与熔断
- 路由与负载均衡、灰度策略
- 服务过滤、聚合与发现
- 权限验证与用户等级策略
- 业务规则与参数检验
- 多级缓存策略
二、Gateway介绍
1.什么是SpringCloud Gateway
网关作为流量的入口,常用的功能包括路由转发、权限检验、限流等。
SpringCloud Gateway是SpringCloud官方推出的第二代网关框架。相比Zuul来说,SpringCloudGateway提供更优秀的性能,更强大的功能。
SpringCloud Gateway是由WebFlux + Netty +Reactor 实现的响应式的API网关。它不能在传统的servlet容器中工作,也不能构建成war包。
SpringCloudGateway旨在微服务架构提供一种简单且有效的API路由的管理方式,并基于Filter的方式提供网关的基本功能,例如说安全认证,监控、限流等等。
官方文档:Spring Cloud Gateway
2.SpringCloud Gateway功能特征
- 基于Spring Framework5、ProjectReactor和SpringBoot2.0进行构建
- 动态路由,能够匹配任何请求属性
- 支持路径重写
- 集成SpringCloud服务发现功能
- 可以集成流控降级功能
- 可以对路由指定易于编写的Predicate(断言)和Filter(过滤器)
3.核心概念
路由 (route)
路由是网关中最基础的部分,路由信息包括一个ID、一个目的uri、一组断言工厂、一组filter组成。如果断言为真,则说明请求的url和配置的路由匹配。
断言 (predicates)
Java8中的断言函数,SpringCloudGateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配 HttpRequest中的任何信息,比如请求头和参数等。
过滤器(filter)
SpringCloudGateway中的filter分为Gateway Filter 和Global Filter。Filter可以对请求和响应进行处理。
三、SpringCloudGateway快速开始
1.导入依赖
<!--SpringCloud Gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2.编写yml配置文件
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
#网关配置
gateway:
#路由规则配置
routes:
- id: order_route #路由的唯一标识,路由到订单服务
uri: http://localhost:8010 #需要转发的地址
predicates:
- Path=/order-service/**
#将http://localhost:8088/order-service/order/add请求路由到↓
#http://localhost:8010/order-service/order/add
filters:
- StripPrefix=1 #StripPrefix内置过滤器中的一种,转发去掉之前第一层路径,把http://localhost:8010/order-service/order/add 变成 http://localhost:8010/order/add
3.这就简单实现了
四、SpringCloudGatewat整合Nacos
在上述中,我们在配置文件中写死了转发路径的地址,接下来我们从注册中心获取此地址。
1.引入依赖
<!--nacos服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.编写yml配置文件
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
#网关配置
gateway:
#路由规则配置
routes:
- id: order_route #路由的唯一标识,路由到订单服务
uri: lb://order-service #需要转发的地址 lb: 使用nacos中的本地负载均衡策略 order-service:服务名
predicates:
- Path=/order-service/**
#将http://localhost:8088/order-service/order/add请求路由到↓
#http://localhost:8010/order-service/order/add
filters:
- StripPrefix=1 #StripPrefix内置过滤器中的一种,转发去掉之前第一层路径,把http://localhost:8010/order-service/order/add 变成 http://localhost:8010/order/add
# Nacos配置
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
简写: 去掉关于路由的配置,自动寻找服务。
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
#网关配置
gateway:
discovery:
locator:
enabled: true #是否启动自动识别nacos服务
# Nacos配置
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
这种方式在实际开发中是不常用的,大家知道即可。
这里就完成了与nacos的整合。
五、路由断言工厂(Route Predicate Factories)
作用:当请求gateway的时候,使用断言对请求进行匹配,如果匹配成功,就进行路由转发,如果匹配失败就返回404。
1.内置断言工厂
SpringCloudGateway包括许多内置的断言工厂,所有这些断言都与http请求的不同属性匹配。
基于Datetime类型的断言工厂
此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory:接收一个日期参数,判断请求日期是否晚于指定日期。
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期。
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内。
- After=2022-01-29T15:49:47.789+08:00[Asia/Shanghai]
- Before=2022-01-29T15:49:47.789+08:00[Asia/Shanghai]
- Between=2022-01-29T15:49:47.789+08:00[Asia/Shanghai], 2022-01-30T15:49:47.789+08:00[Asia/Shanghai]
基于远程地址的断言工厂
RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中。
- RemoteAddr=192.168.1.1/24
基于Cookie的断言工厂
CookieRoutePredicateFactory: 接收两个参数,cookie名字和一个正则表达式。判断请求cookie是否具有给定名称且值与正则表达式匹配。
- Cookie=chocolate, ch.p
基于Header的断言工厂
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。判断请求Header是否具有给定名称且值与正则表达式匹配 。
- Header=X-Request-Id, \d+
基于Host的断言工厂
HostRoutePredicateFactory: 接收一个参数,主机名模式。判断请求的host是否满足匹配规则。
- Host=**.somehost.org,**.anotherhost.org
基于Method的断言工厂
MethodRoutePredicateFactory: 接收一个参数,判断请求类型是否跟指定的类型匹配。
- Method=GET,POST
基于Path请求路径的断言工厂
PathRoutePredicateFactory: 接收一个参数,判断请求的uri部分是否满足路径规则。
- Path=/red/{segment},/blue/{segment}
基于Query请求参数的断言工厂
QueryRoutePredicateFactory:接收两个参数,请求param和正则表达式,判断请求参数是否具有给定名称且值与正则表达式匹配 。
- Query=green
基于路由权重的断言工厂
WeightRoutePredicateFactory:接收一个[组名,权重],然后对于同一个组内的路由按照权重转发。
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
该路由会将约 80% 的流量转发到weightlow.org,将约 20% 的流量转发到weightlow.org。
详细请看官方文档
2.自定义路由断言工厂
自定义路由断言工厂需要继承AbstractRoutePredicateFactory类,重写apply方法的逻辑。在apply方法中可以通过exchange.getRequest()拿到ServletHttpRequest对象,从而可以获取到请求的参数,请求方式,请求头等信息。
注:命名需以RoutePredicateFactory结尾。
package com.zjb.predicates;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
@Component
public class DiyRoutePredicateFactory extends AbstractRoutePredicateFactory<DiyRoutePredicateFactory.Config>{
public static final String NAME = "name";
public DiyRoutePredicateFactory() {
super(DiyRoutePredicateFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList(NAME);
}
@Override
public Predicate<ServerWebExchange> apply(DiyRoutePredicateFactory.Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
if(config.getName().equals("test")){
return true;
}
return false;
}
};
}
@Validated
public static class Config {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
yml配置文件中引用
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
#网关配置
gateway:
#路由规则配置
routes:
- id: order_route #路由的唯一标识,路由到订单服务
uri: lb://order-service #需要转发的地址 lb: 使用nacos中的本地负载均衡策略 order-service:服务名
predicates:
- Diy=name,test
# Nacos配置
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
六、过滤器工厂(Gateway Filter Factories)
1.内置过滤器工厂
SpringCloudGateway内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理,比如添加剔除响应头 ,添加去除参数等。
AddRequestHeader
AddRequestHeader:为原始请求添加header。
filters:
- AddRequestHeader=X-Request-red, blue
AddRequestParameter
AddRequestParameter:为原始请求添加请求参数。
filters:
- AddRequestParameter=red, blue
AddResponseHeader
AddResponseHeader:为原始响应添加header。
filters:
- AddResponseHeader=X-Response-Red, Blue
DedupeResponseHeader
DedupeResponseHeader:剔除响应头中重复的值。
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
因为官方提供的内置过滤器工厂太多了,这里就不在一一列举,详细看官方文档
2.自定义过滤器工厂
自定义过滤器工厂需要继承AbstractNameValueGatewayFilterFactory。
注:自定义名称必须要以GatewayFilterFactory结尾并交给Spring管理。
package com.zjb.filters;
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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
@Component
public class DiyGatewayFilterFactory extends AbstractGatewayFilterFactory<DiyGatewayFilterFactory.Config> {
public static final String NAME = "name";
public static final String VALUE = "value";
public DiyGatewayFilterFactory() {
super(DiyGatewayFilterFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList(NAME,VALUE);
}
@Override
public GatewayFilter apply(DiyGatewayFilterFactory.Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
if (exchange.getRequest().getQueryParams().containsKey(NAME)) {
List<String> values = exchange.getRequest().getQueryParams().get(NAME);
if(values.contains(config.getValue())){
return chain.filter(exchange);
}
}
//返回404
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
return exchange.getResponse().setComplete();
}
};
}
public static class Config {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
3.全局过滤器(Global Filters)
局部过滤器与全局过滤器的区别:
局部:局部过滤器针对某个路由,需要在路由中进行配置。
全局:全局过滤器是针对所有路由请求,一旦定义就会投入使用。
Global Filter接口和GatewayFilter有一样的接口定义,只不过,GlobalFilter会作用于所有路由。
1.内置全局过滤器
详细查看官方文档
2.自定义全局过滤器
实现GlobalFilter接口,并交给Spring管理。
使用场景:日志记录、权限认证等会使用自定义全局过滤器。
package com.zjb.filters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class LogGlobalFilter implements GlobalFilter {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info(exchange.getRequest().getPath().value());
return chain.filter(exchange);
}
}
七、Reactor Netty访问日志
要启用Reactor Netty访问日志,需要设置
-Dreactor.netty.http.server.accessLogEnabled=true
它必须是Java系统属性,而不是 Spring Boot 属性。
八、Gateway跨域设置
通过 yml方式
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': #允许跨域访问的资源
allowedOrigins: "*" #跨域允许来源
allowedMethods: #跨域允许请求方式
- GET
- POST
- PUT
- DELETE
- OPTION
通过java配置方式
package com.zjb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter(){
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",config);
return new CorsWebFilter(source);
}
}
九、Gateway整合Sentinel流控降级
网关作为内部系统外的一层屏障,对内起到一定的保护作用,限流便是其中之一,网关层的限流 可以简单针对不同路由进行限流,也可以针对业务的接口进行限流,或者根据接口的特征分组限流。
1.导入依赖
<!--sentinel-gateway整合依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--sentinel依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.添加配置
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
#网关配置
gateway:
#路由规则配置
routes:
- id: order_route #路由的唯一标识,路由到订单服务
uri: lb://order-service #需要转发的地址 lb: 使用nacos中的本地负载均衡策略 order-service:服务名
predicates:
- Path=/order-service/**
#将http://localhost:8088/order-service/order/add请求路由到↓
#http://localhost:8010/order-service/order/add
filters:
- StripPrefix=1 #StripPrefix内置过滤器中的一种,转发去掉之前第一层路径,把http://localhost:8010/order-service/order/add 变成 http://localhost:8010/order/add
# Nacos配置
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
#配置sentinel
sentinel:
transport:
dashboard: 127.0.0.1:8080
到这里就整合完毕了,网上其它的一些整合文章会去配置一些配置类,是因为使用了官方提供的Spring Cloud Gateway 的适配模块,也就是一下依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>x.y.z</version>
</dependency>
但在2.1.6之后,我们可以直接使用sentinel和gateway的整合包来替代
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
在这个整合包里边已经帮我们做了一系列的配置,就不需要我们在去手动配置。
3.到sentinel控制台中添加流控规则