Spring Cloud Gateway 之 Filter
文章首发于公众号《程序员果果》
地址:https://mp.weixin.qq.com/s/CFqKtIzP1ZzjioVZyGugeQ
简介
网关经常需要对路由请求进行过滤,进行一些操作,如鉴权之后构造头部之类的,过滤的种类很多,如增加请求头、增加请求 参数 、增加响应头和断路器等等功能,这就用到了Spring Cloud Gateway 的 Filter。
作用
当我们有很多个服务时,比如下图中的user-service、goods-service、sales-service等服务,客户端请求各个服务的Api时,每个服务都需要做相同的事情,比如鉴权、限流、日志输出等。
对于这样重复的工作,可以在微服务的上一层加一个全局的权限控制、限流、日志输出的Api Gateway服务,然后再将请求转发到具体的业务服务层。这个Api Gateway服务就是起到一个服务边界的作用,外接的请求访问系统,必须先通过网关层。
生命周期
Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。
- PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
分类
Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。
- GatewayFilter:应用到单个路由或者一个分组的路由上。
- GlobalFilter:应用到所有的路由上。
Gateway filter
过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。过滤器的作用域为特定路由。Spring Cloud Gateway包含许多内置的GatewayFilter工厂。
官方文档都给出了这些过滤器工厂详细的使用案例,在这里我们讲解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>.*), /$\{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<Void> 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<CustomerGatewayFilterFactory.Config> { private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilterFactory.class ); private static final String COUNT_START_TIME = "countStartTime"; @Override public List<String> 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如下:
内置的 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<Void> 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 ...
源码下载 : https://github.com/gf-huanchupk/SpringCloudLearning/tree/master/chapter13/springcloud-gateway-filter
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
基于Tablestore的共享汽车管理平台
前言 目前出行市场主要分为传统和新兴两个阵营:私家车、公共交通、出租车和长租车为传统出行提供服务,共享单车、网约车以及分时租赁共享汽车则是最近几年兴起的出行方式。_图1 出行方式分析_出行者往往是在成本和出行便捷之间权衡选择出行方式。从出行距离来看,在0~10公里以内,共享单车、网约车以及出租车兼具了低成本以及灵活性两个优势,所以往往是出行者的首选。超过100公里以上,私家车以及长租车则会显现其便捷性。但是在10~100公里之间,则存在一个出行服务真空市场。另外,在一二线城市中,10公里以上的出行需求还是非常大的,加上近几年国家的政策导向,国内出现了很多共享汽车租赁平台。按照罗兰贝格估算,2025年有600万辆分时租赁汽车,每辆车每天3-4单,日订单量约2000万单。这个行业还是具备很大的潜力。本文主要介绍如何基于Tablestor
- 下一篇
论程序员的自我修养——我在阿里干了十年开发
究竟是努力重要,还是选择重要?资深阿里技术人毕玄师兄有着自己的见解。 毕玄,阿里巴巴基础设施事业群负责人,资深技术专家。打造了阿里目前使用最为广泛的核心中间件之一的服务框架;设计并带领团队实现了阿里技术发展史上具有里程碑意义的异地多活。 文章不长,但值得品味。师兄的文风就和他的代码一样,简洁洗练。 2007年年底,我入职阿里。工作十年,我看到了各种各样的程序员,也看到了各种各样的成长路线,说说自己的一些观点吧。 第一阶段:拓展视野,择一深入 作为技术人员,在刚起步阶段时,首先需要拓宽自己的技术宽度,对自己所做的项目/产品所涉及的方方面面的技术都应该有所了解。 另外就是学习工程化,让自己真正具备开发商业软件的能力。 在工程化和知识宽度达到一定阶段后,根据自己的兴趣和工作内容有所选择,主要是加强在某一领域的技术深度。 第二阶段:自我评估,选择方向在技
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- Hadoop3单机部署,实现最简伪集群
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Redis,开启缓存,提高访问速度