上海网站备案核验,杭州百度seo,天津品牌网站建设公司,南宁关键词自然排名Zuul 架构图在zuul中#xff0c; 整个请求的过程是这样的#xff0c;首先将请求给zuulservlet处理#xff0c;zuulservlet中有一个zuulRunner对象#xff0c;该对象中初始化了RequestContext#xff1a;作为存储整个请求的一些数据#xff0c;并被所有的zuulfilter共享。…Zuul 架构图在zuul中 整个请求的过程是这样的首先将请求给zuulservlet处理zuulservlet中有一个zuulRunner对象该对象中初始化了RequestContext作为存储整个请求的一些数据并被所有的zuulfilter共享。zuulRunner中还有 FilterProcessorFilterProcessor作为执行所有的zuulfilter的管理器。FilterProcessor从filterloader 中获取zuulfilter而zuulfilter是被filterFileManager所加载并支持groovy热加载采用了轮询的方式热加载。有了这些filter之后zuulservelet首先执行的Pre类型的过滤器再执行route类型的过滤器最后执行的是post 类型的过滤器如果在执行这些过滤器有错误的时候则会执行error类型的过滤器。执行完这些过滤器最终将请求的结果返回给客户端。zuul工作原理源码分析在之前已经讲过如何使用zuul其中不可缺少的一个步骤就是在程序的启动类加上EnableZuulProxy该EnableZuulProxy类代码如下其中引用了ZuulProxyConfiguration跟踪ZuulProxyConfiguration该类注入了DiscoveryClient、RibbonCommandFactoryConfiguration用作负载均衡相关的。注入了一些列的filters比如PreDecorationFilter、RibbonRoutingFilter、SimpleHostRoutingFilter代码如如下它的父类ZuulConfiguration 引用了一些相关的配置。在缺失zuulServlet bean的情况下注入了ZuulServlet该类是zuul的核心类。同时也注入了其他的过滤器比如ServletDetectionFilter、DebugFilter、Servlet30WrapperFilter这些过滤器都是pre类型的。它也注入了post类型的比如 SendResponseFiltererror类型比如 SendErrorFilterroute类型比如SendForwardFilter代码如下初始化ZuulFilterInitializer类将所有的filter 向FilterRegistry注册。而FilterRegistry管理了一个ConcurrentHashMap用作存储过滤器的并有一些基本的CURD过滤器的方法代码如下FilterLoader类持有FilterRegistryFilterFileManager类持有FilterLoader所以最终是由FilterFileManager注入 filterFilterRegistry的ConcurrentHashMap的。FilterFileManager到开启了轮询机制定时的去加载过滤器代码如下Zuulservlet作为类似于Spring MVC中的DispatchServlet,起到了前端控制器的作用所有的请求都由它接管。它的核心代码如下跟踪init()可以发现这个方法为每个请求生成了RequestContext,RequestContext继承了ConcurrentHashMap在请求结束时销毁掉该RequestContextRequestContext的生命周期为请求到zuulServlet开始处理直到请求结束返回结果。 RequestContext类在存储了很多重要的信息包括HttpServletRequest、HttpServletRespons、ResponseDataStream、ResponseStatusCode等。 RequestContext对象在处理请求的过程中一直存在所以这个对象为所有Filter共享。从ZuulServlet的service()方法可知它是先处理pre()类型的处理器然后在处理route()类型的处理器最后再处理post类型的处理器。首先来看一看pre()的处理过程它会进入到ZuulRunner,该类的作用是将请求的HttpServletRequest、HttpServletRespons放在RequestContext类中并包装了一个FilterProcessor代码如下而FilterProcessor类为调用filters的类比如调用pre类型所有的过滤器跟踪runFilters()方法可以发现它最终调用了FilterLoader的getFiltersByType(sType)方法来获取同一类的过滤器然后用for循环遍历所有的ZuulFilter执行了 processZuulFilter()方法跟踪该方法可以发现最终是执行了ZuulFilter的方法最终返回了该方法返回的Object对象。route、post类型的过滤器的执行过程和pre执行过程类似。Zuul默认过滤器默认的核心过滤器一览表Zuul默认注入的过滤器它们的执行顺序在FilterConstants类我们可以先定位在这个类然后再看这个类的过滤器的执行顺序以及相关的注释可以很轻松定位到相关的过滤器也可以直接打开 spring-cloud-netflix-core.jar的 zuul.filters包可以看到一些列的filter现在我以表格的形式列出默认注入的filter.过滤器的order值越小就越先执行并且在执行过滤器的过程中它们共享了一个RequestContext对象该对象的生命周期贯穿于请求可以看出优先执行了pre类型的过滤器并将执行后的结果放在RequestContext中供后续的filter使用比如在执行PreDecorationFilter的时候决定使用哪一个route它的结果的是放在RequestContext对象中后续会执行所有的route的过滤器如果不满足条件就不执行该过滤器的run方法。最终达到了就执行一个route过滤器的run()方法。而error类型的过滤器是在程序发生异常的时候执行的。post类型的过滤在默认的情况下只注入了SendResponseFilter该类型的过滤器是将最终的请求结果以流的形式输出给客户单。现在来看SimpleHostRoutingFilter是如何工作?进入到SimpleHostRoutingFilter类的方法的run()方法核心代码如下查阅这个类的全部代码可知该类创建了一个HttpClient作为请求类并重构了url,请求到了具体的服务得到的一个CloseableHttpResponse对象并将CloseableHttpResponse对象的保存到RequestContext对象中。并调用了ProxyRequestHelper的setResponse方法将请求状态码流等信息保存在RequestContext对象中。现在来看SendResponseFilter是如何工作?这个过滤器的order为1000,在默认且正常的情况下是最后一个执行的过滤器该过滤器是最终将得到的数据返回给客户端的请求。在它的run()方法里有两个方法addResponseHeaders()和writeResponse()即添加响应头和写入响应数据流。其中writeResponse()方法是通过从RequestContext中获取ResponseBody获或者ResponseDataStream来写入到HttpServletResponse中的但是在默认的情况下ResponseBody为null而ResponseDataStream在route类型过滤器中已经设置进去了。具体代码如下如何在zuul上做日志处理由于zuul作为api网关所有的请求都经过这里所以在网关上可以做请求相关的日志处理。 我的需求是这样的需要记录请求的 url,ip地址参数请求发生的时间整个请求的耗时请求的响应状态甚至请求响应的结果等。 很显然需要实现这样的一个功能需要写一个ZuulFliter它应该是在请求发送给客户端之前做处理并且在route过滤器路由之后在默认的情况下这个过滤器的order应该为500-1000之间。那么如何获取这些我需要的日志信息呢找RequestContext,在请求的生命周期里这个对象里存储了整个请求的所有信息。现在编码在代码的注释中做了详细的说明代码如下现在读者也许有疑问如何得到的statrtTime即请求开始的时间其实这需要另外一个过滤器在网络请求route之前(大部分耗时都在route这一步)在过滤器中在RequestContext存储一个时间即可另写一个过滤器代码如下可能还有这样的需求我需要将响应结果也要存储在log中在之前已经分析了在route结束后将从具体服务获取的响应流存储在RequestContext中在SendResponseFilter过滤器写入在HttpServletResponse中最终返回给客户端。那么我只需要在SendResponseFilter写入响应流之前把响应流写入到 log日志中即可那么会引发另外一个问题因为响应流写入到 log后RequestContext就没有响应流了在SendResponseFilter就没有流输入到HttpServletResponse中导致客户端没有任何的返回数据那么解决的办法是这样的InputStream inputStream RequestContext.getCurrentContext().getResponseDataStream();InputStream newInputStream copy(inputStream);transerferTolog(inputStream);RequestContext.getCurrentContext().setResponseDataStream(newInputStream);从RequestContext获取到流之后首先将流 copy一份将流转化下字符串存在日志中再set到RequestContext中 这样SendResponseFilter就可以将响应返回给客户端。这样的做法有点影响性能如果不是字符流可能需要做更多的处理工作。原文地址https://dwz.cn/9tKgRhJF作者方志朋