gateway 过滤器执行顺序_Gateway过滤器

Spring Cloud Gateway 提供一种默认转发的能力,只要将 Spring Cloud Gateway 注册到服务中心,Spring Cloud Gateway 默认就会代理服务中心的所有服务。

Spring Cloud Gateway 的 Filter 的生命周期只有两个:“pre” 和 “post”。

PRE: 这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

为什么需要网关?

有很多个服务时,比如下图中的user-service、goods-service、sales-service等服务,客户端请求各个服务的Api时,每个服务都需要做相同的事情,比如鉴权、限流、日志输出等。

在微服务的上一层加一个全局的权限控制、限流、日志输出的Api Gatewat服务,然后再将请求转发到具体的业务服务层。这个Api Gateway服务就是起到一个服务边界的作用,外接的请求访问系统,必须先通过网关层。

GatewayFilterChain

/*** 网关过滤链表接口

* 用于过滤器的链式调用

* Contract to allow a {@linkWebFilter} to delegate to the next in the chain.*/

public interfaceGatewayFilterChain {/*** 链表启动调用入口方法

* Delegate to the next {@codeWebFilter} in the chain.

*@paramexchange the current server exchange

*@return{@codeMono} to indicate when request handling is complete*/Monofilter(ServerWebExchange exchange);

}

/*** 网关过滤的链表,用于过滤器的链式调用

* 过滤器链表接口的默认实现,

* 包含2个构建函数:

* 1.集合参数构建用于初始化构建链表

* 2. index,parent参数用于构建当前执行过滤对应的下次执行的链表*/

private static class DefaultGatewayFilterChain implementsGatewayFilterChain {/*** 当前过滤执行过滤器在集合中索引*/

private final intindex;/*** 过滤器集合*/

private final Listfilters;public DefaultGatewayFilterChain(Listfilters) {this.filters =filters;this.index = 0;

}/*** 构建

*@paramparent 上一个执行过滤器对应的FilterChain

*@paramindex 当前要执行过滤器的索引*/

private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, intindex) {this.filters =parent.getFilters();this.index =index;

}public ListgetFilters() {returnfilters;

}/***@paramexchange the current server exchange

*@return

*/@Overridepublic Monofilter(ServerWebExchange exchange) {return Mono.defer(() ->{if (this.index

GatewayFilter filter = filters.get(this.index);//构建当前索引的下一个过滤器的FilterChain

DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);//调用过滤器的filter方法执行过滤器

returnfilter.filter(exchange, chain);

}else{//当前索引大于等于过滤集合大小,标识所有链表都已执行完毕,返回空

return Mono.empty(); //complete

}

});

}

}

过滤器的GatewayFilterChain 执行顺序

通过GatewayFilter集合构建顶层的GatewayFilterChain

调用顶层GatewayFilterChain,获取第一个Filter,并创建下一个Filter索引对应的GatewayFilterChain

调用filter的filter方法执行当前filter,并将下次要执行的filter对应GatewayFilterChain传入。

GatewayFilter

public interface GatewayFilter extendsShortcutConfigurable {

String NAME_KEY= "name";

String VALUE_KEY= "value";/*** 过滤器执行方法

* Process the Web request and (optionally) delegate to the next

* {@codeWebFilter} through the given {@linkGatewayFilterChain}.

*@paramexchange the current server exchange

*@paramchain provides a way to delegate to the next filter

*@return{@codeMono} to indicate when request processing is complete*/Monofilter(ServerWebExchange exchange, GatewayFilterChain chain);

}

OrderedGatewayFilter

/*** 排序的网关路由过滤器,用于包装真实的网关过滤器,已达到过滤器可排序

*@authorSpencer Gibb*/

public class OrderedGatewayFilter implementsGatewayFilter, Ordered {//目标过滤器

private finalGatewayFilter delegate;//排序字段

private final intorder;public OrderedGatewayFilter(GatewayFilter delegate, intorder) {this.delegate =delegate;this.order =order;

}

@Overridepublic Monofilter(ServerWebExchange exchange, GatewayFilterChain chain) {return this.delegate.filter(exchange, chain);

}

}

OrderedGatewayFilter实现类主要目的是为了将目标过滤器包装成可排序的对象类型。是目标过滤器的包装类

GatewayFilterAdapter

/*** 全局过滤器的包装类,将全局路由包装成统一的网关过滤器*/

private static class GatewayFilterAdapter implementsGatewayFilter {/*** 全局过滤器*/

private finalGlobalFilter delegate;publicGatewayFilterAdapter(GlobalFilter delegate) {this.delegate =delegate;

}

@Overridepublic Monofilter(ServerWebExchange exchange, GatewayFilterChain chain) {return this.delegate.filter(exchange, chain);

}

}

GatewayFilterAdapter实现类主要目的是为了将GlobalFilter过滤器包装成GatewayFilter类型的对应。是GlobalFilter过滤器的包装类

生命周期

客户端的请求先经过“pre”类型的filter,然后将请求转发到具体的业务服务,比如上图中的user-service,收到业务服务的响应之后,再经过“post”类型的filter处理,最后返回响应到客户端。Spring Cloud Gateway 的 Filter 分为两种:GatewayFilter 与 GlobalFilter。GlobalFilter 会应用到所有的路由上,而 GatewayFilter 将应用到单个路由或者一个分组的路由上。

gateway filter

过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。过滤器可以限定作用在某些特定请求路径上。 Spring Cloud Gateway包含许多内置的GatewayFilter工厂。

GatewayFilter工厂都是在配置文件application.yml中配置,只需要在配置文件配置GatewayFilter Factory的名称,而不需要写全部的类名,比如AddRequestHeaderGatewayFilterFactory只需要在配置文件中写AddRequestHeader,而不是全部类名。在配置文件中配置的GatewayFilter Factory最终都会相应的过滤器工厂类处理。

AddRequestHeader GatewayFilter Factory

movie:

uri: lb://consumer-movie

spring:

application:

name: gateway-client

cloud:

gateway:

routes:-id: route_service_one

uri: ${movie.uri} # uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称

filters:- AddRequestHeader=X-Request-Foo, Bar

predicates:- Path=/movie/**

AddRequestHeaderGatewayFilterFactory的源码如下:

public class AddRequestHeaderGatewayFilterFactory extendsAbstractNameValueGatewayFilterFactory {

@OverridepublicGatewayFilter apply(NameValueConfig config) {return (exchange, chain) ->{

ServerHttpRequest request=exchange.getRequest().mutate()

.header(config.getName(), config.getValue())

.build();returnchain.filter(exchange.mutate().request(request).build());

};

}

}

根据旧的ServerHttpRequest创建新的 ServerHttpRequest ,在新的ServerHttpRequest加了一个请求头,然后创建新的 ServerWebExchange ,提交过滤器链继续过滤。

RewritePath GatewayFilter Factory

spring:

cloud:

gateway:

routes:-id: rewritepath_route

uri: https://blog.csdn.net

predicates:- Path=/foo/**filters:

- RewritePath=/foo/(?.*), /$\{segment}

profiles: rewritepath_route

所有的/foo/**开始的路径都会命中配置的router,并执行过滤器的逻辑,配置了RewritePath过滤器工厂,此工厂将/foo/(?.*)重写为{segment},然后转发到https://blog.csdn.net。比如在网页上请求localhost:8081/foo/forezp,此时会将请求转发到https://blog.csdn.net/forezp的页面,比如在网页上请求localhost:8081/foo/forezp/1,页面显示404,就是因为不存在https://blog.csdn.net/forezp/1这个页面

自定义过滤器

在spring Cloud Gateway中,过滤器需要实现GatewayFilter和Ordered2个接口。写一个RequestTimeFilter,代码如下:

packagecom.smart.filter;importlombok.extern.slf4j.Slf4j;importorg.springframework.cloud.gateway.filter.GatewayFilter;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.core.Ordered;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;

@Slf4jpublic class RequestTimeFilter implementsGatewayFilter,Ordered {private final static String REQUEST_TIME_BEGIN="requestTimeBegin";

@Overridepublic Monofilter(ServerWebExchange exchange, GatewayFilterChain chain) {

exchange.getAttributes().put(REQUEST_TIME_BEGIN,System.currentTimeMillis());return chain.filter(exchange).then(Mono.fromRunnable(()->{

Long startTime=exchange.getAttribute(REQUEST_TIME_BEGIN);if(startTime!=null){

log.info(exchange.getRequest().getURI().getRawPath()+":"+(System.currentTimeMillis()-startTime)+"ms");

}

}));

}

@Overridepublic intgetOrder() {return 0;

}

}

Ordered中的int getOrder()方法是来给过滤器设定优先级别的,值越大则优先级越低。还有一个filterI(exchange,chain)方法,在该方法中,先记录了请求的开始时间,并保存在ServerWebExchange中,此处是一个“pre”类型的过滤器,然后再chain.filter的内部类中的run()方法中相当于"post"过滤器,在此处打印了请求所消耗的时间。然后将该过滤器注册到router中,代码如下:

@BeanpublicRouteLocator customerRouteLocator(RouteLocatorBuilder builder) {returnbuilder.routes()

.route(r-> r.path("/movie/**")

.filters(f->f.hystrix(config -> config.setName("movie.service")

.setFallbackUri("forward:/movie/fallback"))

.filter(newRequestTimeFilter()))

.uri(uri)).build();

}

自定义过滤器工厂

自定义过滤器工厂,可以在配置文件中配置过滤器。GatewayFilterfactory的层级如下:

过滤器工厂的顶级接口是GatewayFilterFactory,有2个两个较接近具体实现的抽象类,分别为AbstractGatewayFilterFactory和AbstractNameValueGatewayFilterFactory,这2个类前者接收一个参数,比如它的实现类RedirectToGatewayFilterFactory;后者接收2个参数,比如它的实现类AddRequestHeaderGatewayFilterFactory类。需要将请求的日志打印出来,需要使用一个参数,这时可以参照RedirectToGatewayFilterFactory的写法。

global filter

GlobalFilter : 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。

该GlobalFilter会校验请求中是否包含了请求参数“token”,如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。代码如下:

packagecom.smart.filter;importlombok.extern.slf4j.Slf4j;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.core.Ordered;importorg.springframework.http.HttpStatus;importorg.springframework.util.StringUtils;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;

@Slf4jpublic class TokenFilter implementsGlobalFilter,Ordered {

@Overridepublic Monofilter(ServerWebExchange exchange, GatewayFilterChain chain) {

String token= exchange.getRequest().getQueryParams().getFirst("token");if(StringUtils.isEmpty(token)){

log.info("token is empty...");

exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);returnexchange.getResponse().setComplete();

}returnchain.filter(exchange);

}

@Overridepublic intgetOrder() {return -100;

}

}

TokenFilter需要实现GlobalFilter和Ordered接口,然后根据ServerWebExchange获取ServerHttpRequest,然后根据ServerHttpRequest中是否含有参数token,如果没有则完成请求,终止转发,否则执行正常的逻辑。

需要将TokenFilter在工程的启动类中注入到Spring Ioc容器中,代码如下:

@BeanpublicTokenFilter tokenFilter(){return newTokenFilter();

}

参考:

https://blog.csdn.net/forezp/article/details/85057268

https://windmt.com/2018/05/08/spring-cloud-14-spring-cloud-gateway-filter/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值