目录
AddRequestHeaderGatewayFilterFactory过滤器工厂
AbstractGatewayFilterFactory 实现自定义过滤器工厂
上一篇描述了服务网关Gateway 的工作流程,断言、和生命周期;Predicate 的作用是,请求到来之后,如果匹配到相应的断言,则会交给相应的路由进行处理,在路由处理前后,请求还会交给Filter 处理;
过滤器作用
在服务网关执行流程中可以了解到,过滤器起到了非常重要的作用,pre 类型的filter 可以实现日志输出,权限校验,限流等功能,post 类型的filter 可以实现响应头、响应内容、流量监控等功能;
同时,服务网关的过滤器可以分为两种,一种是作用在单个路由的网关过滤器(Gateway Filter),他的作用和断言类似;一种是作用于所有路由的,他是全局过滤器(Global Gateway Filter);
网关过滤器工厂
GateWayFilter 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用于当前路由上,或者通过spring.cloud.default-filters 配置在全局中,作用在所有的路由上;
在服务网关中包含了多个内置的网关过滤器工厂(Gateway Filter),配置和断言类似,在配置文件中配置即可生效;服务网关内置的Gateway Filter Factory 如下:
AddRequestHeaderGatewayFilterFactory过滤器工厂
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar
为原始请求添加名为X-Request-Foo 值为Bar 请求头
其他过滤器工厂具体的配置方式可以参考官网进行配置,此处不一一举例 ;Gateway官方文档
过滤器工厂核心API
exchange.getRequest().mutate().xxx //修改request
exchange.mutage().xxx // 修改exchange
chain.filter() // 将filter 传递给下一个filter
exchange.getResponse() //获取响应
自定义过滤器工厂
上边列举了Gateway 内置过滤器工厂,但是在业务中,如果上述不符合我们的需求,我们需要自定义过滤器工厂;比如我们根据配置开关决定是否打印log日志需求
通过跟踪内置的过滤器工厂源码发现,实现过滤器工厂的方式有两种,一种是继承AbstractGatewayFilterFactory,实现类参考RedirectToGatewayFilterFactory;另一种是继承AbstractNameValueGatewayFilterFactory实现类参考AddRequestHeaderGatewayFilterFactory。两种方式的区别就是前者接收一个参数,后者接收两个参数;注意:自定义过滤器工厂类后缀要以xxxGatewayFilterFactory 结尾,配置文件配置属性名称为xxx;
下面基于第一种方式实现根据配置判断是否打印log日志;
AbstractGatewayFilterFactory 实现自定义过滤器工厂
package com.corn.gateway;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
/**
* @Author corn
* @Date 2021/3/30 20:57
*/
@Slf4j
@Component
public class PrintLogGatewayFilterFactory extends AbstractGatewayFilterFactory<PrintLogGatewayFilterFactory.Config> {
public PrintLogGatewayFilterFactory(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
return chain.filter(exchange).then(
Mono.fromRunnable(()->{
if (config.isPrint()){
// 打印路径地址
log.info(exchange.getRequest().getURI().getRawPath());
}
})
);
});
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("print");
}
@Data
public static class Config{
boolean print;
}
}
配置
spring:
cloud:
gateway:
routes:
- id: printlog_route
uri: lb:\\nacos-alibaba
filters:
# 开关控制是否打印请求url
- PrintLog=false
全局过滤器
上边讲述了作用于具体路由下的过滤器Gateway Fliter ,而GloubaFilter 则不需要在配置文件中配置,便可以作用在所有的路由上,GloubaFilter 将请求的业务及路由Url转换成真实的业务服务请求地址和核心过滤器,不需要配置,系统在初始化的时候,作用在每个路由上;注意:order 值越小,优先级越高,越靠前执行
GloubaFilter 过滤器包含了:
1 Combined Global Filter and GatewayFilter Ordering
2 Forward Routing Filter 路径转发过滤器
3 LoadBalancerClient Filter 负载均衡客户端过滤器
4 Netty Routing Filter http客户端过滤器
5 Netty Write Response Filter http端客户端过滤器
6 RouteToRequestUrl Filter 转发路由过滤器
7 Websocket Routing Filter webSocket 转发过滤器
自定义全局过滤器
实现自定义全局过滤器校验参数和打印请求执行时间
package com.corn.gateway;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.net.URI;
/**
* @Author corn
* @Date 2021/3/28 15:27
* 自定义网关
* 实现判断请求参数中是否含有version 参数,如果不含有这返回404
*/
@Slf4j
@Component
public class MyGatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 获取请求参数
MultiValueMap<String, String> queryParams = request.getQueryParams();
String version = queryParams.getFirst("version");
if (StringUtils.isEmpty(version)){
log.info("请求参数中缺少version 参数,返回404" );
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return response.setComplete();
}
exchange.getAttributes().put("startTime",System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(()->{
Long startTime = exchange.getAttribute("startTime");
if (null != startTime){
log.info("请求地址:{},响应时间:{}ms",exchange.getRequest().getURI().getRawPath(),(System.currentTimeMillis()-startTime));
}
})
);
}
/**
*@描述 值越小,优先级越高
*@参数
*@返回值
*@创建人 corn
*@创建时间 2021/3/28
*/
@Override
public int getOrder() {
return 10;
}
}
Gateway 限流
未引入Gateway之前,针对我们的微服务接口,如果我们需要对流量进行管控,可以通过sentinel 来实现。在Gateway 中,官方给我们提供了RequestRateLimiterGatewayFilterFactory 这个类,这个类使用了redis 和lua 脚本实现令牌桶算法进行限流;
配置方式如下:
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
编写规则
package com.corn.gateway;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
/**
* 令牌算法限流,根据请求url进行限流
* @Author corn
* @Date 2021/3/28 18:23
*/
@Configuration
public class Raonfiguration {
/**
* 按照Path限流
*
* @return key
*/
@Bean
public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest()
.getPath()
.toString()
);
}
// /**
// *@描述 针对来源ip 进行限流
// *@参数
// *@返回值
// *@创建人 corn
// *@创建时间 2021/3/30
// */
// @Bean
// public KeyResolver ipKeyResolver() {
// return exchange -> Mono.just(
// exchange.getRequest()
// .getHeaders()
// .getFirst("X-Forwarded-For")
// );
// }
//
// /**
// *@描述 根据用户进行限流
// *@参数
// *@返回值
// *@创建人 corn
// *@创建时间 2021/3/30
// */
// @Bean
// public KeyResolver userKeyResolver() {
// return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
// }
}
添加配置
spring:
cloud:
gateway:
routes:
- id: printlog_route
uri: lb:\\nacos-alibaba
filters:
# 开关控制是否打印请求url
- name: RequestRateLimiter
args:
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 令牌桶的上限
redis-rate-limiter.burstCapacity: 2
# 使用SpEL表达式从Spring容器中获取Bean对象
key-resolver: "#{@pathKeyResolver}"
总结
结合上一篇博文,讲述了服务网关Gateway 的原理和作用,然后梳理了两个核心组件断言和过滤器,并基于两个核心组件实现简单配置的demo ;