Gateway过滤器原理和生命周期

16 篇文章 1 订阅
7 篇文章 0 订阅

1. 过滤器原理和生命周期

所有的开源框架实现过滤器的模式都是大同小异的,通过一种类似职责链的方式,传统的职责链模式中的事件会传递指直到有一个处理对象接手,而过滤器和传统的职责链有点不同,所有过滤器都要进行过滤和处理,一路走到底,直到被最后一个过滤器处理

1.1. 过滤器的实现方式

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

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

这里面有两个关键信息

  • ServerWebExchange:这是Spring封装的HTTP request-response的交互协议,从中我们可以获取request和resposne中的各种请求参数,也可以向其中添加内容
  • GatewayFilterChain:他是过滤器的调用链,在方法结束的时候我们需要将exchange对象传入调用链中的下一个对象

1.2. 过滤器的执行阶段

Gateway是通过Filter中的代码来实现类似Pre和Post的效果的

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

Pre类型Post类型一个在过滤器执行前一个在执行后

过滤器可以排顺序的

在Gateway中可以实现 org.springframework.core.Ordered接口,来指定过滤器的执行顺序,通过实现getOrder方法

public int getOrder(){
  return 0;
}
// Pre类型的过滤器来说,数字越大表示优先级越高,也就越早被执行。但对于Post类型过滤器,则是数字越小越先被执行

1.3. 过滤器示例

Header过滤器

这个系列有很多组过滤器,可以将信息加入到指定Header

.filters(f -> f.addResponseHeader("name","gateway-server"))
//相当于向header中添加一个name属性,对应的值是gateway-server

StripPrefix过滤器

这是个比较常用的过滤器,他的作用是去掉部分URL路径

.route(r -> r.path("/gateway-test/**")
						 .filters(f -> f.stripPrefix(1))
       			 .uri("lb://FEIFN-SERVICE/")
)
//假如HTTP请求访问的是/gateway-test/sample/update,如果没有StripPreix过滤器,那么转发到FEIGN-SERVIC服务的访问路径也是一样的//FEIGN-SERVICE/gateway-test/sample/update,如果添加了这个过滤器,gateway就会根据stripPrefix(1)中的配置截取URL的路径,比如这里设置的是1,那么就去掉一个前缀,最终发送给后台服务的路径就变成//FEIGN-SERVICE/sample/update

PrefixPath过滤器

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

.route(r -> r.path("/gateway-test/**")
						 .filters(f -> f.prefixPath("go"))
       			 .uri("lb://FEIGN-SERVICE/")
)
//假如我们访问的路径是/gateway-test/sample,如果使用这个过滤器就会变成//FEIGN-SERVICE/go/gateway-test/sample

RedirectTo过滤器

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

.filters(f -> f.redirect(304,"https://www.baidu.com"))
//Caused by: java.lang.IllegalArgumentException: status must be a 3xx code, but was 404

2. 自定义过滤器实现接口计时功能

去gateway-server项目组进行修改,创建一个filter的package

package com.icodingedu.springcloud.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Slf4j
@Component
public class TimerFilter implements GatewayFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //给接口计时并能输出log
        StopWatch timer = new StopWatch();
        //开始计时
        timer.start(exchange.getRequest().getURI().getRawPath());
        //我们也可以对调用链进行加工,手工放入请求参数
        exchange.getAttributes().put("requestTimeBegin",System.currentTimeMillis());
        return chain.filter(exchange).then(
            //这里就是执行完过滤进行调用的地方
            Mono.fromRunnable(() -> {
                timer.stop();;
                log.info(timer.prettyPrint());
            })
        );
    }

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

去到GatewayConfiguration里设置自定义filter

@Configuration
public class GatewayConfiguration {

    @Autowired
    private TimerFilter timerFilter;

    @Bean
    @Order
    public RouteLocator customerRouters(RouteLocatorBuilder builder){
        LocalDateTime ldt = LocalDateTime.of(2020,10,24,21,05,10);
        return builder.routes()
                .route(r -> r.path("/gavinjava/**")
                             .and().method(HttpMethod.GET)
                             .filters(f -> f.stripPrefix(1)
                                            .addResponseHeader("java-param","gateway-config")
                                            .filter(timerFilter)
                             )
                             .uri("lb://FEIGN-CLIENT")
                )
                .route(r -> r.path("/secondkill/**")
                             .and().after(ZonedDateTime.of(ldt, ZoneId.of("Asia/Shanghai")))
                             .filters(f -> f.stripPrefix(1))
                             .uri("lb://FEIGN-CLIENT/")
                )
                .build();
    }
}

测试后结果如下

# 这里的百分比指的是这个接口执行的时间占整个执行链路的百分比
# 1秒=1000000000(ns)9个0
---------------------------------------------
ns         %     Task name
---------------------------------------------
1775599286  100%  /sayhello

上面定义的是针对具体的route的filter,我们也可以定义一个全局的filter直接应用在所有的route上,只需要把filter的继承修改下即可,所有route就可以自动加载了,不用调用

@Slf4j
@Component
public class TimerFilter implements GlobalFilter, Ordered

将原来config中引用的timeFilter都去掉即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值