> 文章列表 > 微服务架构-服务网关(Gateway)-过滤器原理和生命周期

微服务架构-服务网关(Gateway)-过滤器原理和生命周期

微服务架构-服务网关(Gateway)-过滤器原理和生命周期

过滤器原理和生命周期

前面我们学习了Gateway的路由和断言功能,这一节我们来了解下过滤器原理,再挑几个常用的过滤器学习一下。

1、过滤器的工作模式

所有开源框架实现过滤器的模式都是大同小异,通过一种类似职责链的方式,传统的职责链模式中的事件会传递直到有一个处理对象接手,而过滤器和传统的职责链有点不同,它更像是足球队开场握手一样,所有队员一字排开,你要从头到尾依次和所有球员握过手。

Gateway中的过滤器也是一样的模型,他们经过优先级的排列,所有网关调用请求从最高优先级的过滤器开始。一路走到头,直到被最后一个过滤器处理。

2、过滤器的实现方式

在Gateway中实现一个过滤器非常简单,只要实现GatewayFilter接口的默认方法就好了

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 随意发挥return chain.filter(exchange);    
}

这里面有两个关键信息:
ServerWebExchange: 这是Spring封装的HTTP request-response交互协议,从中我们可以获取request和response中的各种请求参数,也可以向其中添加内容;

GatewayFilterChain: 它是过滤器的调用链,在方法结束的时候我们需要将exchange对象传入调用链中的下一个对象。

3、过滤器的执行阶段

不同于Spring Cloud中上一代网关组件Zuul里对过滤器的Pre和Post的定义,Gateway是通过Filter中的代码来实现类似Pre和Post的效果。

Pre和Post是指代当前过滤器的执行阶段,Pre是在下一个过滤器之前被执行,Post是在过滤器执行过后再执行,我们在Gateway Filter中也可以同时定义Pre和Post执行逻辑。

3.1、Pre类型

我们就拿 AddResponseHeaderGatewayFilterFactory 举例,它可以向 Response 中添加 Header 信息:

@Override
public GatewayFilter apply(NameValueConfig config) {return (exchange, chain) -> {exchange.getResponse().getHeaders().add(config.getName(), config.getValue());return chain.filter(exchange);};
}

这里的具体执行方法是定义在调用“chain.filter()方法之前,也就是在转发到下级调用链路之前执行的,因此可以理解为一个Pre类型的过滤器。

3.2、Post类型

我们拿SetStatusGatewayFilterFactory举例,它在过滤器执行完毕之后,将制定的HTTP status返回给调用方。

return chain.filter(exchange).then(Mono.fromRunnable(() -> {// 这里是业务逻辑}));

这个过滤器的主要逻辑在then方法中,then是一个回调函数,在下级调用链路都完成以后再执行,因此这类过滤器可以看做是Post Filter。

4、过滤器排座次

在Gateway中我们可以通过实现org.spingfamewok core.Ordered接口,来给过滤器指定执行顺序,比下面的代实现了Ordered接口方法,将过滤器执行顺序设置为0,

@Override
public int getOrder() {return 0;
}

Pre类型的过滤器来说,数字越大表示优先级越高,也就越早被执行,但对于Post类型的过滤器,则是数字越小越先被执行。

5、过滤器示例

5.1、Header过滤器

这个系列有很多组过滤器,AddRequestHeader和AddResponseHeader,分别向Request和Response里加入指定Header,相应的RemoveRequestHeader和RemoveResponseHeader分别做移除操作,用法也很简单:

.filters(f -> f.addResponseHeader("who", "gateway-header"))

上面的例子会向header中添加一个who的属性,对应的值是gateway-header。

5.2、StripPrefix过滤器

这是个比较常用的过滤器,它的作用是去掉部分URL路径。比如我们的过滤器配置如下:

.route(r -> r.path("/gateway-test/**").filters(f -> f.stripPrefix(1)).uri("lb://FEIGN-SERVICE-PROVIDER/")
)

假如HTTP请求访问的是 /gateway-test/sample/update,如果没有StripPrefix过滤器,那么转发到FEIGN-SERVICE-PROVIDER服务的访问路径也是一样的。

当我们添加了这个过滤器之后,Gateway就会根据 stripPrefix(1)中的值取URL中的路径,比如这里我们设置的是1,那么就去掉一个前缀,最终发送给后台服务的路径变成了"sample/update"

5.3、PrefixPath过滤器

它和StripPrefix的作用是完全相反的,会在请求路径的前面加入前缀

.route(r -> r.path("/gateway-test/**").filters(f -> f.prefixPath("go")).uri("lb://FEIGN-SERVICE-PROVIDER/")
)

比如说我们访问“/gateway-test/sample”的时候,上面例子中配置的过滤器就会把请求发送到"go/gateway-test/sample”

5.4、RedirectTo过滤器

它可以把收到特定状态码的请求重定向到一个指定网址:

.filters(f -> f.redirect(302, "https://www.study.com/"))

上面的例子接收HTTP status code和URL两个参数,如果请求结果是404,则重定向到第二个参数指定的项面,这个功能也可以做统一异常处理,将UnauthorizedForbidden请求重定向到登录页面。

5.5、SaveSession过滤器

我们知道微服务是无状态的会话,所以大多都不依赖session机制,但是如果你有分布式session的需求,比说某些功能是基于spring-session和spring-securiy来实现的,那么这个过滤器或许对你有用,它在调用服务之前都会强制保存session:

.filters(f -> f.saveSession())