spring-cloud-gateway之GlobalFilter

引言

全局过滤器无疑是作用于所有经过网关转发的请求的,对于设计者来说被设计成全局过滤器实现的功能组件也即是设计者认为此功能是网关所必备的功能组件,这点非常重要的,对于引入spring-cloud-gateway做二次封装的时候,我们应该秉承作者的设计思想,即如果需要增加一个全局过滤组件的时候应该实现一个全局过滤器(即实现GlobalFilter接口)。

我们可以通过gateway内嵌的endpoint来查看GlobalFilter列表(/actuator/gateway/globalfilters):

GlobalFilters

从api结果可以看到一个全局过滤器列表,并且没一个过滤器都有一个排序id,gateway是通过同时实现Ordered接口来实现控制过滤器的过滤顺序的,其中id越小其代表的优先级越高。

预览GlobalFilter、Ordered

从GlobalFilter接口的声明来看,其作用及其明确,就是定义了一个filter行为,用于拦截web请求和实现一些跨领域,与应用无关的需求,比如安全验证、超时判断和其他的一些功能性行为。

Ordered接口是spring框架的一个规范接口,用于元素排序或者需要有序的对象实现该接口。

从endpoint api的结果中我们将org.springframework.cloud.gateway.filter.包下的全局过滤器罗列出来,org.iblog.enhance.gateway.filter.**包下的均是增强实现的拦截功能(本文对于自增实现只简单提及,后续章节会详细解释)。

  • org.iblog.enhance.gateway.filter.MarkRequestFilter@75c589f2: -2147483647,
  • org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@7c8f803d: -2147482648,
  • org.iblog.enhance.gateway.filter.logging.LoggingRequestBodyFilter@6db04a6: -1000,
  • org.iblog.enhance.gateway.filter.convert.ConvertRequestBodyFilter@1bba9862: -900,
  • org.iblog.enhance.gateway.filter.extract.ExtractRequestBodyFilter@565c887e: -800,
  • org.iblog.enhance.gateway.filter.async.AsyncProcessingFilter@1317ac2c: -700,
  • org.iblog.enhance.gateway.filter.convert.ConvertResponseBodyFilter@451a4187: -25,
  • org.iblog.enhance.gateway.filter.logging.LoggingResponseBodyFilter@8f374de: -20,
  • org.iblog.enhance.gateway.filter.extract.ExtractResponseBodyFilter@5c215642: -10,
  • org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@4e26c308: -1,
  • org.springframework.cloud.gateway.filter.ForwardPathFilter@7c6ab057: 0,
  • org.springframework.cloud.gateway.filter.GatewayMetricsFilter@1f7557fe: 0,
  • org.iblog.enhance.gateway.filter.logging.LoggingResponseStatusFilter@426913c4: 1,
  • org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@176e839e: 10000,
  • org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@363ba634: 10100,
  • org.iblog.enhance.gateway.filter.logging.LoggingRealPathFilter@416c1b0: 10101
  • org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@7ce4498f: 2147483646,
  • org.springframework.cloud.gateway.filter.ForwardRoutingFilter@4ac0d49: 2147483647,

展示官方的架构设计图,所有的filtter则按照图中filter模块进行工作,处于handler-proxied模块之间,而且是一个双向的工作关系:


MarkRequestFilter | 优先级:-2147483647

这是我个人实现的一个GlobalFilter,这是为自己在后续功能实现而准备的一个过滤器;为每一个request第一时间分配一个uniqueue_key,并且set到exchange的attributes属性列表当中。

MarkRequestFilter

AdaptCachedBodyGlobalFilter | 优先级:-2147482648

gateway内置的一个GlobalFilter,其作用是从exchange的attributes中或者cachedRequestBody属性值作为request的body,注意使用此功能首先必须预设cachedRequestBody属性至attributes中,这个必须要手动设置,无法通过配置来设置的,可以在优先级更高的filter中来设置cachedRequestBody;当然也可以自己写一个FilterFactory类来做成配置化;我个人觉得暂时这个过滤器作用场景还不太明确,可以不用太考虑。

AdaptCachedBodyGlobalFilter

LoggingRequestBodyFilter | 优先级: -1000

这个GlobalFilter是我自己实现的,其功能是对于gateway透传的每一个请求进行日志跟踪并且记录至持久化层,这里会将request的基础信息logging下来,初始化一个日志实体存储。

ConvertRequestBodyFilter | 优先级: -900

属于enhance-gateway自己的实现,这个功能在还是比较常见的,尤其是面向B端客户需求的时候,会有接口对接时报文转换的需求,在这个filter中就是实现了这种应用无感知的转换。

ExtractRequestBodyFilter | 优先级:-800

属于enhance-gateway自己的实现,这个功能与ConvertRequestBodyFilter类似,只不过这里不是报文转换,而是实现从报文中抽取一些关键词,这个filter从某个角度上其实可以考虑与ConvertRequestBodyFilter合并成一个filter实现,这个取决于需求的复杂性。

AsyncProcessingFilter | 优先级:-700

属于enhance-gateway自己的实现,这里是一种针对性的需求,接受调用方一个请求,而调用方对于处理结果的处理进度没有时间敏感或者任务处理时常过长的情况,采用即时反馈而异步作处理的方式来处理。

ConvertResponseBodyFilter | 优先级:-25

属于enhance-gateway自己的实现,与ConvertRequestBodyFilter对称,对于response的body做输出报文转换的需求,这个视具体应用场景来定,算是一种比较常见的开发场景。

LoggingResponseBodyFilter | 优先级:-20

属于enhance-gateway自己的实现,与LoggingRequestBodyFilter对称,将response的body记录进日志需求中。

ExtractResponseBodyFilter | 优先级:-10

属于enhance-gateway自己的实现,与ExtractRequestBodyFilter对称,将response的body中的关键词记录进日志需求中。

NettyWriteResponseFilter | 优先级:-1

将Netty代理调用的response数据流写入ServerHttpResponse的body中,此外这个是一个先调用后发生的行为动作,流式编程的典型范例,也是验证了之前所说的filter的链式调用是一个对称的模型,对于优先级越高的行为定义其真实发生其实越晚,NettyWriteResponseFilter将结果数据流写入ServerHttpResponse中发生在NettyRouting获取到远程调用的结果数据流之后,当NettyRouting拿到结果数据流之后会将其写入当前请求exchange的attributes中,写入发生在return语句。建议调试阶段可以将log level设置为trace,这样可以清楚地观察请求的行为发生轨迹。

NettyWriteResponseFilter

ForwardPathFilter | 优先级:0

这是一个条件过滤器,只有当请求的header scheme为forward的时候才会发生,否则会忽略没有任何作用,当有转发需求的时候会将request的请求path修改,从而修改了请求的目的地址。

ForwardPathFilter

GatewayMetricsFilter | 优先级:0

metrics实现,对于每个请求记录其发生时间戳,包括开始和结束时间以及请求状态,gateway集成了micrometer框架来实现的,个人觉得这个metrics实现并不是非常完美,micrometer实现其实依赖了dropwizard-metricsdropwizard-metrics与微服务框架dropwizard之间可以无缝集成,效果非常好,监控数据是非常全面的,而micrometer实现比较精简,感兴趣的读者可以学习一下dropwizard的使用,轻量级容易上手开发。

GatewayMetricsFilter

LoggingResponseStatusFilter | 优先级:1

属于enhance-gateway自己的实现,与GatewayMetricsFilter记录请求结果状态一致,不过我是基于依赖做的二次开发,无法复用已有的逻辑部分;这里有个需要注意的地方,就是其优先级必须发生在NettyWriteResponseFilter之后,也就是要大于-1才能获取到status,否则取到的会是null。

RouteToRequestUrlFilter | 优先级:10000

RouteToRequestUrlFilter是必须的全局过滤器,这个会在你通过注册中心配置一个下游服务的时候起作用,请求进来的时候path的前缀是gateway的服务地址(ip+port或者是域名),但是对于真实的请求调用,必须将其uri映射至服务id上;比如将path的192.168.20.134:10080映射至服务lb://{serviceId};而对于绝对路径配置的服务exchange的GATEWAY_ROUTE_ATTR属性将会是null,直接过滤到下一个过滤器,而不会发生path的真实映射。

RouteToRequestUrlFilter

LoadBalancerClientFilter | 优先级:10100

LoadBalancerClientFilter负责服务真实ip的映射,主要针对对个服务节点的情况进行负载均衡,默认采用的netflix-ribbon作为负载均衡器,首先如果scheme不是服务节点映射的话直接过滤,获取服务节点,69行的choose函数是真实负载均衡发生的函数,获取一个本次选出的服务server instance(如果是单节点则无负载计算),然后将服务的真实ip+port替换掉path中的lb://{serviceId}前缀。

LoadBalancerClientFilter

LoggingRealPathFilter | 优先级:10101

属于enhance-gateway自己的实现,发生在LoadBalancerClientFilter之后便可以获取当前请求的真实path。

WebsocketRoutingFilter | 优先级:2147483646

WebsocketRoutingFilter过滤器实现了gateway对于websocket的支持,内部通过websocketClient实现将一个http请求协议换转成websocket,实现调用方无感知的请求websocket的服务,只需要将schme设置成ws或者wss这么简单。

WebsocketRoutingFilter

ForwardRoutingFilter | 优先级:2147483647

ForwardRoutingFilter是一个结束操作,经过filter chain的链式调用,最终将exchange交还给web handler做http请求处理。

ForwardRoutingFilter

总结

源码当中还有两个GlobalFilter,分别是WebClientHttpRoutingFilter、WebClientWriteResponseFilter,/actuator/gateway/globalfilters结果当中不包含它们原因是没有在GatewayAutoConfiguration依赖注入,作者还没启用。

整个filter的调用通过Ordered接口捆绑成一个链式结构,并且通过reactor流式编程将代码精简,这是一个非常优秀的思想,如果在自己的服务当中采用此编码模型的话,会使得代码清晰很多,感兴趣的读者可以参考学习reactor-core

本文主要分析了gateway的GlobalFilter的源码实现,对于enhance-gateway实现的GlobalFilter只是提及,这个是一种前后对应的设计思想,并不是很完美,计划在后续通过一篇独立的文章探讨自己的思想,并进行优化。

注:enhance-gateway参考地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容