Zuul 原理 与 源码分析

Zuul 原理分析

在这里插入图片描述

在 zuul 中, 整个请求过程是这样的
首先将请求 交给 zuulservlet 处理
zuulservlet 中 有个 zuulRunner 对象,该对象 初始化了 RequestContext: 作为存储整个请求(每个)的一些数据,并被所有的zuulFilter 共享,
zuulRunner 对象总 还有 FilterProcesspor,FilterProcessor 作为执行所有的zuulFilter 的过滤器
FilterProcessor 从 filterLoader中获取 zuulFilter,而zuulFilter 是被 filtererFileManager 所加载,并支持groovy热加载,采用了轮询的方式热加载

有了这些filter之后,zuulservlet首先执行Pre类型的过滤器,然后执行route类型的过滤器,最后执行的是post类型的过滤器,
如果在执行这些过滤器有错误的时候,则会执行error类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。

Zuul 源码分析(部分分析,后续还要看)

在之前已经讲过,如何使用zuul,其中不可缺少的一个步骤就是在程序的启动类加上@EnableZuulProxy,该EnableZuulProxy类代码如下:

    @EnableCircuitBreaker
    @EnableDiscoveryClient
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(ZuulProxyConfiguration.class)
    public @interface EnableZuulProxy {
    }

其中,引用了ZuulProxyConfiguration,跟踪ZuulProxyConfiguration,该类注入了DiscoveryClient、RibbonCommandFactoryConfiguration用作负载均衡相关的。

注入了一些列的filters,比如PreDecorationFilter、RibbonRoutingFilter、SimpleHostRoutingFilter,代码如如下:

    @Bean
        public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
            return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(), this.zuulProperties,
                    proxyRequestHelper);
        }

        // route filters
        @Bean
        public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
                RibbonCommandFactory<?> ribbonCommandFactory) {
            RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers);
            return filter;
        }

        @Bean
        public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties) {
            return new SimpleHostRoutingFilter(helper, zuulProperties);
        }

ZuulProxyConfiguration 他的父类 ZuulConfiguration,引用了一些相关的配置。在缺失zuulServlet bean的情况下注入了ZuulServlet,该类是zuul的核心类。

      @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;
        }

同时也注入了其他的过滤器,比如ServletDetectionFilter、DebugFilter、Servlet30WrapperFilter,这些过滤器都是pre类型的。

@Bean
        public ServletDetectionFilter servletDetectionFilter() {
            return new ServletDetectionFilter();
        }

        @Bean
        public FormBodyWrapperFilter formBodyWrapperFilter() {
            return new FormBodyWrapperFilter();
        }

        @Bean
        public DebugFilter debugFilter() {
            return new DebugFilter();
        }

        @Bean
        public Servlet30WrapperFilter servlet30WrapperFilter() {
            return new Servlet30WrapperFilter();
        }

它也注入了post类型的,比如 SendResponseFilter,error类型,比如 SendErrorFilter,route类型比如SendForwardFilter,代码如下:


        @Bean
        public SendResponseFilter sendResponseFilter() {
            return new SendResponseFilter();
        }

        @Bean
        public SendErrorFilter sendErrorFilter() {
            return new SendErrorFilter();
        }

        @Bean
        public SendForwardFilter sendForwardFilter() {
            return new SendForwardFilter();
        }

初始化ZuulFilterInitializer类,将所有的filter 向FilterRegistry注册。

        @Configuration
        protected static class ZuulFilterConfiguration {

            @Autowired
            private Map<String, ZuulFilter> filters;

            @Bean
            public ZuulFilterInitializer zuulFilterInitializer(
                    CounterFactory counterFactory, TracerFactory tracerFactory) {
                FilterLoader filterLoader = FilterLoader.getInstance();
                FilterRegistry filterRegistry = FilterRegistry.instance();
                return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry);
            }

        }

而FilterRegistry管理了一个ConcurrentHashMap,用作存储过滤器的,并有一些基本的CURD过滤器的方法,代码如下:

     public class FilterRegistry {

        private static final FilterRegistry INSTANCE = new FilterRegistry();

        public static final FilterRegistry instance() {
            return INSTANCE;
        }

        private final ConcurrentHashMap<String, ZuulFilter> filters = new ConcurrentHashMap<String, ZuulFilter>();

        private FilterRegistry() {
        }

        public ZuulFilter remove(String key) {
            return this.filters.remove(key);
        }

        public ZuulFilter get(String key) {
            return this.filters.get(key);
        }

        public void put(String key, ZuulFilter filter) {
            this.filters.putIfAbsent(key, filter);
        }

        public int size() {
            return this.filters.size();
        }

        public Collection<ZuulFilter> getAllFilters() {
            return this.filters.values();
        }

    }

FilterLoader类持有FilterRegistry,FilterFileManager类持有FilterLoader,所以最终是由FilterFileManager注入 filterFilterRegistry的ConcurrentHashMap的。FilterFileManager到开启了轮询机制,定时的去加载过滤器,代码如下:

      void startPoller() {
            poller = new Thread("GroovyFilterFileManagerPoller") {
                public void run() {
                    while (bRunning) {
                        try {
                            sleep(pollingIntervalSeconds * 1000);
                            manageFiles();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            poller.setDaemon(true);   // 守护线程 定时 去加载过滤器
            poller.start();
        }

Zuulservlet作为类似于Spring MVC中的DispatchServlet,起到了前端控制器的作用,所有的请求都由它接管。它的核心代码如下:

       @Override
        public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
            try {
                init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

                // 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();
            }
        }

跟踪init(),可以发现这个方法为每个请求生成了RequestContext,RequestContext继承了ConcurrentHashMap<String, Object>,在请求结束时销毁掉该RequestContext,RequestContext的生命周期为请求到zuulServlet开始处理,直到请求结束返回结果。
RequestContext类在存储了很多重要的信息,包括HttpServletRequest、HttpServletRespons、ResponseDataStream、ResponseStatusCode等。 RequestContext对象在处理请求的过程中,一直存在,所以这个对象为所有Filter共享。

      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));

      }


     public void preRoute() throws ZuulException {
        FilterProcessor.getInstance().preRoute();
    }

从ZuulServlet的service()方法可知,它是先处理pre()类型的处理器,然后在处理route()类型的处理器,最后再处理post类型的处理器。

首先来看一看pre()的处理过程,它会进入到ZuulRunner,该类的作用是将请求的HttpServletRequest、HttpServletRespons放在RequestContext类中,并包装了一个FilterProcessor,代码如下:

而FilterProcessor类为调用filters的类,比如调用pre类型所有的过滤器:

      public void preRoute() throws ZuulException {
            try {
                runFilters("pre");
            } catch (ZuulException e) {
                throw e;
            } catch (Throwable e) {
                throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
            }
        }

跟踪runFilters()方法,可以发现,它最终调用了FilterLoader的getFiltersByType(sType)方法来获取同一类的过滤器,然后用for循环遍历所有的ZuulFilter,执行了 processZuulFilter()方法,跟踪该方法可以发现最终是执行了ZuulFilter的方法,最终返回了该方法返回的Object对象。

     public Object runFilters(String sType) throws Throwable {
            if (RequestContext.getCurrentContext().debugRouting()) {
                Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
            }
            boolean bResult = false;
            List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
            if (list != null) {
                for (int i = 0; i < list.size(); i++) {
                    ZuulFilter zuulFilter = list.get(i);
                    Object result = processZuulFilter(zuulFilter);
                    if (result != null && result instanceof Boolean) {
                        bResult |= ((Boolean) result);
                    }
                }
            }
            return bResult;
        }

route、post类型的过滤器的执行过程和pre执行过程类似。

其他源码分析 后续会继续

1 Ribbon 源码分析
2 Hystrix 源码分析
3 Feign 源码分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值