gatewayfilter详解_Spring Cloud Gateway 之 Filter

简介

网关经常需要对路由请求进行过滤,进行一些操作,如鉴权之后构造头部之类的,过滤的种类很多,如增加请求头、增加请求 参数 、增加响应头和断路器等等功能,这就用到了Spring Cloud Gateway 的 Filter。

作用

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

b599d2bda4dacc35e35948d06250d457.png

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

23302212ec67f0b4633b3107123410e6.png

生命周期

Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。

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

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

6fa6cc411876f66ca2a1de7a96a111e2.png

分类

Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。

GatewayFilter:应用到单个路由或者一个分组的路由上。

GlobalFilter:应用到所有的路由上。

Gateway filter

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

e5e8e05efad408cdf512e8c373279e38.png

官方文档都给出了这些过滤器工厂详细的使用案例,在这里我们讲解2个来演示下使用。

AddRequestHeader GatewayFilter Factory

application.yml如下:

spring:

cloud:

gateway:

routes:

- id: add_request_header_route

uri: http://httpbin.org:80/get

filters:

- AddRequestHeader=X-Request-Foo, Bar

predicates:

- Method=GET

过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Foo,值为Bar。

###RewritePath GatewayFilter Factory

在Nginx服务启中有一个非常强大的功能就是重写路径,Spring Cloud Gateway默认也提供了这样的功能,这个功能是Zuul没有。

application.yml如下:

spring:

cloud:

gateway:

routes:

- id: rewritepath_route

uri: http://httpbin.org

predicates:

- Path=/foo/**

filters:

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

所有的/foo/**开始的路径都会命中配置的router。

请求http://httpbin.org/foo/get ,会转到http://httpbin.org/get,结果如下:

{

"args": {

},

"headers": {

"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",

"Accept-Encoding": "gzip, deflate, br",

"Accept-Language": "zh-CN,zh;q=0.9,zh-TW;q=0.8",

"Cache-Control": "max-age=0",

"Cookie": "Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1550127915; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22168eada0ded53b-0d5d8c3ba9b7a2-10316653-1296000-168eada0dee3ad%22%2C%22%24device_id%22%3A%22168eada0ded53b-0d5d8c3ba9b7a2-10316653-1296000-168eada0dee3ad%22%2C%22props%22%3A%7B%7D%7D; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1",

"Forwarded": "proto=http;host=\"127.0.0.1:8080\";for=\"127.0.0.1:62278\"",

"Host": "httpbin.org",

"Upgrade-Insecure-Requests": "1",

"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36",

"X-Forwarded-Host": "127.0.0.1:8080",

"X-Forwarded-Prefix": "/foo"

},

"origin": "127.0.0.1, 124.74.78.150, 127.0.0.1",

"url": "https://127.0.0.1:8080/get"

}

自定义GatewayFilter

Spring Cloud Gateway内置了的过滤器工厂,足够是大部分场景使用,而且我们可以实现GatewayFilter和Ordered 这两个接口来自定义过滤器。代码如下:

/**

* 统计某个或者某种路由的处理时长

*/

public class CustomerGatewayFilter implements GatewayFilter, Ordered {

private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilter.class );

private static final String COUNT_START_TIME = "countStartTime";

@Override

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

exchange.getAttributes().put(COUNT_START_TIME, Instant.now().toEpochMilli() );

return chain.filter(exchange).then(

Mono.fromRunnable(() -> {

long startTime = exchange.getAttribute(COUNT_START_TIME);

long endTime=(Instant.now().toEpochMilli() - startTime);

log.info(exchange.getRequest().getURI().getRawPath() + ": " + endTime + "ms");

})

);

}

@Override

public int getOrder() {

return 0;

}

}

上述代码中,getOrder()方法是来给过滤器设定优先级别的,值越大则优先级越低。需要将自定义的GatewayFilter 注册到router中,代码如下:

@Bean

public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {

return builder.routes()

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

.filters(f -> f.filter(new CustomerGatewayFilter())

.addResponseHeader("X-Response-test", "test"))

.uri("http://httpbin.org:80/get")

.id("customer_filter_router")

)

.build();

}

使用 curl 测试,命令行输入:

curl http://localhost:8080/customer/555

控制台输出如下:

2019-02-22 10:39:47.840 INFO 1310 --- [ctor-http-nio-3] com.gf.config.CustomerGatewayFilter : /customer/555: 314ms

自定义过滤器工厂

自定义GatewayFilter又有两种实现方式,一种是上面的直接 实现GatewayFilter接口,另一种是 自定义过滤器工厂(继承AbstractGatewayFilterFactory类) , 选择自定义过滤器工厂的方式,可以在配置文件中配置过滤器了。

@Component

public class CustomerGatewayFilterFactory extends AbstractGatewayFilterFactory {

private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilterFactory.class );

private static final String COUNT_START_TIME = "countStartTime";

@Override

public List shortcutFieldOrder() {

return Arrays.asList("enabled");

}

public CustomerGatewayFilterFactory() {

super(Config.class);

log.info("Loaded GatewayFilterFactory [CustomerGatewayFilterFactory]");

}

@Override

public GatewayFilter apply(Config config) {

return (exchange, chain) -> {

if (!config.isEnabled()) {

return chain.filter(exchange);

}

exchange.getAttributes().put(COUNT_START_TIME, System.currentTimeMillis());

return chain.filter(exchange).then(

Mono.fromRunnable(() -> {

Long startTime = exchange.getAttribute(COUNT_START_TIME);

if (startTime != null) {

StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath())

.append(": ")

.append(System.currentTimeMillis() - startTime)

.append("ms");

sb.append(" params:").append(exchange.getRequest().getQueryParams());

log.info(sb.toString());

}

})

);

};

}

public static class Config {

/**

* 控制是否开启统计

*/

private boolean enabled;

public Config() {}

public boolean isEnabled() {

return enabled;

}

public void setEnabled(boolean enabled) {

this.enabled = enabled;

}

}

}

application.yml 中 网关路由配置如下:

spring:

profiles: elapse_route

cloud:

gateway:

routes:

- id: elapse_route

uri: http://httpbin.org:80/get

filters:

- Customer=true

predicates:

- Method=GET

使用 curl 测试,命令行输入:

curl http://localhost:8080/customer?foo=1

控制台输出如下:

2019-02-23 13:37:08.769 INFO 1700 --- [ctor-http-nio-3] c.g.config.CustomerGatewayFilterFactory : /customer: 585ms params:{foo=[1]}

Global filter

Spring Cloud Gateway框架内置的GlobalFilter如下:

aec46d64f5c8d6ae6997d1ac26729e1b.png

内置的 GlobalFilter 能够满足大多数的需求了,但是如果遇到特殊情况,内置满足不了我们的需求,还可以自定义GlobalFilter。

自定义GlobalFilter

下面的我们自定义一个GlobalFilter,去校验所有请求的请求参数中是否包含“token”,如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。

/**

* Token 校验全局过滤器

*/

@Component

public class AuthorizeFilter implements GlobalFilter, Ordered {

private static final Logger log = LoggerFactory.getLogger( AuthorizeFilter.class );

private static final String AUTHORIZE_TOKEN = "token";

@Override

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

String token = exchange.getRequest().getQueryParams().getFirst( AUTHORIZE_TOKEN );

if ( StringUtils.isBlank( token )) {

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

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

return exchange.getResponse().setComplete();

}

return chain.filter(exchange);

}

@Override

public int getOrder() {

return 0;

}

}

使用 curl 测试,命令行输入:

curl http://localhost:8080/customer?foo=1

控制台输出如下:

token is empty ...

欢迎关注我的公众号《程序员果果》,关注有惊喜~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值