• ZuulServlet源码分析及ZuulFilter加载


    参考
    https://yq.aliyun.com/wenji/2...
    https://blog.csdn.net/lds2227...

    1.声明ZuulServlet

    @Configuration
    @EnableConfigurationProperties({ZuulProperties.class})
    @ConditionalOnClass(ZuulServlet.class)  --->[ConditionalOnClass详解](https://412887952-qq-com.iteye.com/blog/2395065)
    @Import(ServerPropertiesAutoConfiguration.class)
    public class ZuulConfigurationCustom {
        @Autowired
       protected ZuulProperties zuulProperties;
       
        @Bean
        @ConditionalOnMissingBean(name = "zuulServlet")
        public ServletRegistrationBean zuulServlet() {
            ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),this.zuulProperties.getServletPattern());
            // The whole point of exposing this servlet is to provide a route that doesn't
            // buffer requests.
            servlet.addInitParameter("buffer-requests", "false");
            return servlet;
        }
    }

    以上通过 ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),this.zuulProperties.getServletPattern()); 使用配置的URL mapping实例化一个Servlet,其中URL mapping在 application.properties 文件中配置,例如:

    #拦截路径
    zuul.servletPath=/openapi/

    以上实现了 /openapi/ 被 ZuulServlet 处理的逻辑。

    2.ZuulServlet处理逻辑

    当请求path为 /openapi/ 的请求进入网关后,就会被 ZuulServlet 处理,以下为 ZuulServlet 处理流程:

    public class ZuulServlet extends HttpServlet {
    
        private static final long serialVersionUID = -3374242278843351500L;
        private ZuulRunner zuulRunner; //ZuulServlet逻辑真正的实现类
    
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            super.init(config);
    
            String bufferReqsStr = config.getInitParameter("buffer-requests");
            boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;
    
            zuulRunner = new ZuulRunner(bufferReqs);
        }
    
        @Override
        public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
            try {
                init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);//将servletRequest和servletResponse存入RequestContext(RequestContext.getCurrentContext())
    
                // Marks this request as having passed through the "Zuul engine", as opposed to servlets
                // explicitly bound in web.xml, for which requests will not have the same data attached
                RequestContext context = RequestContext.getCurrentContext();
                context.setZuulEngineRan();
    
                try {
                    preRoute();
                } catch (ZuulException e) {
                    error(e);
                    postRoute();
                    return;
                }
                try {
                    route();
                } catch (ZuulException e) {
                    error(e);
                    postRoute();
                    return;
                }
                try {
                    postRoute();
                } catch (ZuulException e) {
                    error(e);
                    return;
                }
    
            } catch (Throwable e) {
                error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
            } finally {
                RequestContext.getCurrentContext().unset();
            }
        }
    
        /**
         * executes "post" ZuulFilters
         *
         * @throws ZuulException
         */
        void postRoute() throws ZuulException {
            zuulRunner.postRoute();
        }
    
        /**
         * executes "route" filters
         *
         * @throws ZuulException
         */
        void route() throws ZuulException {
            zuulRunner.route();
        }
    
        /**
         * executes "pre" filters
         *
         * @throws ZuulException
         */
        void preRoute() throws ZuulException {
            zuulRunner.preRoute();
        }
    
        /**
         * initializes request
         *
         * @param servletRequest
         * @param servletResponse
         */
        void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
            zuulRunner.init(servletRequest, servletResponse);
        }
    
        /**
         * sets error context info and executes "error" filters
         *
         * @param e
         */
        void error(ZuulException e) {
            RequestContext.getCurrentContext().setThrowable(e);
            zuulRunner.error();
        }
    }
    View Code

    以上 service() 方法中 init() 会在ZuulRunner中将 servletRequest 和 servletResponse 存入 RequestContext(RequestContext.getCurrentContext()) ,如下:

    public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
    
            RequestContext ctx = RequestContext.getCurrentContext();
            if (bufferRequests) {
                ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
            } else {
                ctx.setRequest(servletRequest);
            }
    
            ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
        }

     ZuulServlet 的 preRoute() 方法通过 zuulRunner 的 preRoute() 实现; zuulRunner 的 preRoute() 通过调用 FilterProcessor.getInstance().preRoute() 实现; FilterProcessor 的 preRoute() 执行自身 runFilters("pre") 方法执行 prefilter ; runFilters("pre") 方法会从 FilterLoader.getInstance().getFiltersByType(sType) 中过滤出pre filter循环执行 processZuulFilter(zuulFilter) ,以下为服务调用层次关系。

    ZuulServlet.preRoute();
      |_____ zuulRunner.preRoute();
            |_____ FilterProcessor.getInstance().preRoute();
                    |_____ runFilters("pre");
                           |_____ List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
                           |       |_____ Collection<ZuulFilter> filters = filterRegistry.getAllFilters(); 
                           |_____ processZuulFilter(zuulFilter); 
                                   |_____ ZuulFilterResult result = filter.runFilter();//执行filter,其中包括filter是否需要执行(shouldFilter())
    //filterRegistry为单例模式,通过FilterRegistry.instance()获取实例,getAllFilters()方法获取类型为ConcurrentHashMap<String, ZuulFilter>的filters。

    3.ZuulFilter 加载

    针对上述代码中的 FilterRegistry 中的filters需要在项目启动时,显示声明进行初始化:

     @Configuration
        protected static class ZuulFilterConfiguration {
    
            //按类型将所有ZuulFilter注入到map中
            @Autowired
            private Map<String, ZuulFilter> filters;
    
            @Bean
            public ZuulFilterInitializer zuulFilterInitializer() {
                return new ZuulFilterInitializer(this.filters); //将项目中的ZuulFilter存入FilterRegistry.instance()的filters中
            }
    
        }

    其中 ZuulFilterInitializer 为实现了 ServletContextListener 类,会根据项目声明在项目启动时进行对 FilterRegistry.instance() 的 filters 进行初始化。

    @CommonsLog
    public class ZuulFilterInitializer implements ServletContextListener {
    
        private Map<String, ZuulFilter> filters;
    
        public ZuulFilterInitializer(Map<String, ZuulFilter> filters) {
            this.filters = filters;
        }
    
        @Override
        public void contextInitialized(ServletContextEvent sce) {
    
            log.info("Starting filter initializer context listener");
    
            // FIXME: mocks monitoring infrastructure as we don't need it for this simple app
            MonitoringHelper.initMocks();
    
            FilterRegistry registry = FilterRegistry.instance();
    
            for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
                registry.put(entry.getKey(), entry.getValue());
            }
        }
        ..........后继代码省略
     }

    4.其他

     ZuulServlet 的 route() 、 postRoute() 、 error(e) 方法执行逻辑与 preRoute() 相同。

  • 相关阅读:
    单片机4*4按键数码管显示0-9
    netbeans设置语言
    单片机中断的应用
    Linux 命令总结
    vc6.0快捷键大全
    怎么使PUTTY一直连接
    mysql alter 效率
    treeview
    Gitbook
    linux 进程监控软件 supervisor
  • 原文地址:https://www.cnblogs.com/Java-Script/p/11091530.html
Copyright © 2020-2023  润新知