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-metrics,dropwizard-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参考地址

你可能感兴趣的:(spring-cloud-gateway之GlobalFilter)