当前位置: 首页 > 科技观察

SpringCloud源码分析(四)Zuul:核心过滤器

时间:2023-03-14 15:21:57 科技观察

通过前面的介绍,我们对Zuul的第一印象通常是这样的:它包括请求的路由和过滤两个功能,其中路由功能负责转发外部对具体微服务实例的请求是实现对外访问统一入口的基础;而filter函数则负责干预请求的处理,是实现请求校验、服务聚合等功能的基础。然而实际上,路由功能在实际运行时,其路由映射和请求转发是由几个不同的过滤器完成的。其中,路由映射主要由pre类型的过滤器完成,将请求路径与配置的路由规则进行匹配,找到需要转发的目标地址;请求转发的部分由路由类型的过滤器完成。转发pre类型过滤器得到的路由地址。因此过滤器可以说是Zuul实现API网关功能的核心组件。每个进入Zuul的HTTP请求都会经过一系列的过滤器处理链,得到请求响应并返回给客户端。接下来我们就通过本文来深入了解一下SpringCloudZuul的过滤器吧!以下内容节选自《Spring Cloud微服务实战》,稍作加工。1、过滤器SpringCloudZuul中实现的过滤器必须包含四个基本特征:过滤器类型、执行顺序、执行条件、具体操作。这些元素似乎很熟悉。其实就是ZuulFilter接口中定义的四个抽象方法:StringfilterType();intfilterOrder();booleanshouldFilter();对象运行();它们各自的含义和作用总结如下:filterType:该函数需要返回一个字符串来表示过滤器的类型,这个类型就是HTTP请求过程中定义的各个阶段。Zuul中默认定义了四种不同生命周期的过滤器类型,如下:pre:可以在请求被路由之前调用。路由:路由请求时调用。post:在路由和错误过滤器之后调用。error:在处理请求时发生错误时调用。filterOrder:过滤器的执行顺序由int值定义,值越小,优先级越高。shouldFilter:返回一个boolean类型来判断过滤器是否应该被执行。我们可以使用这种方法来指定过滤器的有效范围。run:过滤器的具体逻辑。在这个函数中,我们可以实现自定义的过滤逻辑,来决定是拦截当前请求而不进行后续路由,还是在请求路由返回结果后才对处理结果进行处理。2、请求生命周期在上一节中,我们对SpringCloudZuul中的过滤器类型filterType做了一些简单的介绍。Zuul默认定义了四种不同的过滤器类型,涵盖了外部HTTP请求的到来。API网关,直到返回请求结果的完整生命周期。下图来自Zuul官方WIKI中的请求生命周期图,描述了一个HTTP请求到达API网关后如何在不同类型的过滤器之间流动的详细过程。从上图中我们可以看出,当一个外部HTTP请求到达API网关服务时,首先会进入第一阶段pre,这里会被pre类型的过滤器处理。主要目的是在请求路由之前做一些预处理,比如请求验证。完成pre-typefilter处理后,请求进入第二阶段路由,也就是前面说的路由请求转发阶段。该请求将由路由类型过滤器处理。这里的具体处理内容是将外部请求转发给具体服务实例上传的过程,当服务实例返回所有请求结果后,路由阶段完成,请求进入第三阶段post,此时request会被post类型的filter处理,这些filter在处理的时候不仅可以获取请求信息,还可以获取服务实例的返回信息,所以在post类型的filter中,我们可以进行一些处理或者处理结果的转换。另外还有一个特殊的stageerror,只有在上面三个stage出现异常时才会触发,但是它最终的流程还是post类型的filter,因为需要通过postfilter才能传递最终的结果返回给请求客户端(实际实现中还是有一些差异,后面会介绍)。3、核心过滤器在SpringCloudZuul中,为了让API网关组件更容易使用,在HTTP请求生命周期的每个阶段默认实现了一批核心过滤器,它们会由API网关服务启动时间自动加载并启用。我们可以在源码中查看和理解,它们定义在spring-cloud-netflix-core模块的org.springframework.cloud.netflix.zuul.filters包下。如上图所示,默认启用的过滤器包括三个不同生命周期的过滤器。这些过滤器非常重要,可以帮助我们理解Zuul对外请求处理的流程,帮助我们如何在此基础上进行构建。扩展过滤器,完成自身系统需要的功能。下面,我们将对这些过滤器一一做一些详细的介绍:1.前置过滤器ServletDetectionFilter:它的执行顺序是-3,也就是最后执行的过滤器。这个过滤器会一直执行,主要用来检测当前请求是通过Spring的DispatcherServlet处理的还是通过ZuulServlet处理的。它的检测结果会以Boolean类型保存在当前请求上下文的isDispatcherServletRequest参数中,以便在后续的过滤器中,我们可以通过RequestUtils.isDispatcherServletRequest()和RequestUtils.isZuulServletRequest()方法进行判断,实现不同的处理。一般情况下,外部发送到API网关的请求都会由Spring的DispatcherServlet处理,只是通过/zuul/路径访问的请求会绕过DispatcherServlet,由ZuulServlet处理,ZuulServlet主要用于处理大文件上传。另外,对于ZuulServlet的访问路径/zuul/,我们可以通过zuul.servletPath参数进行修改。Servlet30WrapperFilter:它的执行顺序是-2,是第二个执行的过滤器。目前的实现会对所有请求生效,主要是将原来的HttpServletRequest包装成一个Servlet30RequestWrapper对象。FormBodyWrapperFilter:它的执行顺序是-1,是第三个执行的filter。此过滤器仅对两种类型的请求有效。第一种是Content-Type为application/x-www-form-urlencoded的请求。第二种是multipart/form-data的Content-Type,被Spring的DispatcherServlet处理过的请求(使用ServletDetectionFilter的处理结果)。这个过滤器的主要作用是将符合要求的请求体包装成一个FormBodyRequestWrapper对象。DebugFilter:它的执行顺序为1,是第四个执行的过滤器。filter会根据配置参数zuul.debug.request和request中的debug参数来决定是否执行filter中的操作。而它的具体操作内容是将当前请求上下文中的debugRouting和debugRequest参数设置为true。由于这两个值可以在同一个请求的不同生命周期访问到,所以我们可以通过这两个值在后续的过滤器中定义一些调试信息,这样当线上环境出现问题的时候,这些调试信息可以通过请求参数来激活,帮助分析问题。另外,我们还可以通过zuul.debug.parameter自定义请求参数中的debug参数。PreDecorationFilter:它的执行顺序是5,也就是pre阶段最后执行的filter。过滤器会判断当前请求上下文中是否存在forward.to和serviceId参数。如果不存在,则执行具体的过滤操作(如果存在,则说明当前请求已经处理完毕,因为这两条信息是根据当前请求的路由信息??加载的)。而它的具体操作内容是对当前请求做一些预处理,比如:匹配路由规则,在请求上下文中设置请求的基本信息,设置路由匹配结果等信息,这些都会用到为后续过滤服务端处理的重要依据,我们可以通过RequestContext.getCurrentContext()获取这些信息。另外,我们在这个实现中还可以找到一些处理HTTP头请求的逻辑,其中包含一些熟悉的头字段,比如:X-Forwarded-Host,X-Forwarded-Port。另外,这些头域的记录是通过zuul.addProxyHeaders参数来控制的,而这个参数的默认值为true,所以Zuul在请求跳转的时候会默认在请求中加上X-Forwarded-*头域,包括:X-Forwarded-Host、X-Forwarded-Port、X-Forwarded-For、X-Forwarded-Prefix、X-Forwarded-Proto。我们还可以通过设置zuul.addProxyHeaders=false来关闭这些标头的添加。《Spring Cloud实战小贴士:Zuul处理Cookie和重定向》文中提到的添加敏感头信息忽略头信息的操作调用是在PreDecorationFilter过滤器中实现的。2、路由过滤器RibbonRoutingFilter:它的执行顺序是10,是路由阶段第一个执行的过滤器。该过滤器只处理请求上下文中带有serviceId参数的请求,即只对通过serviceId配置路由规则的请求生效。过滤器的执行逻辑是面向服务路由的核心。它使用Ribbon和Hystrix向服务实例发起请求,并返回服务实例的请求结果。SimpleHostRoutingFilter:它的执行顺序是100,是route阶段执行的第二个filter。该过滤器只处理请求上下文中带routeHost参数的请求,即只对通过url配置路由规则的请求生效。这个过滤器的执行逻辑是直接向routeHost参数的物理地址发起请求。从源码中我们可以知道请求是直接通过httpclient封装实现的,没有使用Hystrix命令进行封装,所以这类请求没有线程隔离和断路器保护。SendForwardFilter:它的执行顺序是500,是route阶段执行的第三个filter。该过滤器只处理请求上下文中带有forward.to参数的请求,即用于处理路由规则中forward本地跳转配置。3.PostfilterSendErrorFilter:它的执行顺序为0,是post阶段第一个执行的filter。仅当请求上下文包含error.status_code参数(由先前执行的过滤器设置的错误代码)并且尚未被此过滤器处理时,才会执行此过滤器。这个过滤器的具体逻辑是利用请求上下文中的错误信息组织转发请求到API网关/错误错误端点,产生错误响应。SendResponseFilter:它的执行顺序是1000,是post阶段执行的filter。该过滤器会检查请求上下文中是否包含与请求响应、响应数据流或响应体相关的头信息,只有包含其中之一才会执行处理逻辑。这个过滤器的处理逻辑是利用请求上下文的响应信息来组织需要回传给客户端的响应内容。具体代码这里就不一一列举了,读者可以根据类名查看源码了解详细的处理过程。下图是对上述过滤器按照顺序、名称、功能、类型进行了综合整理,可以帮助我们在自定义或扩展过滤器时参考和综合考虑整个请求生命周期的处理过程。【本文为专栏作家“翟永超”原创稿件,转载请联系作者获得授权】点此查看该作者更多好文