zoukankan      html  css  js  c++  java
  • Zuul的核心源码解析

    在 Zuul中, 整个请求的过程是这样的,首先将请求给zuulservlet处理,zuulservlet中有一个
    zuulRunner对象,该对象中初始化了RequestContext:作为存储整个请求的一些数据,并被所有的
    zuulfilter共享。zuulRunner中还有 FilterProcessor,FilterProcessor作为执行所有的zuulfilter的管理
    器。FilterProcessor从filterloader 中获取zuulfilter,而zuulfilter是被filterFileManager所加载,并支
    持groovy热加载,采用了轮询的方式热加载。有了这些filter之后,zuulservelet首先执行的Pre类型的

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

    (1)初始化

    SpringCloud对Zuul的封装使得发布一个ZuulServer无比简单,根据自动装载原则可以在 spring-
    cloud-netflix-zuul-2.1.0.RELEASE.jar 下找到 spring.factories

    ZuulServerAutoConfiguration ,ZuulProxyAutoConfiguration 是Zuul服务端的自动配置类,这些
    配置类究竟负责什么工作,我们继续来看

    @Configuration
    @Import({RestClientRibbonConfiguration.class, OkHttpRibbonConfiguration.class,
    HttpClientRibbonConfiguration.class, HttpClientConfiguration.class})
    @ConditionalOnBean({Marker.class})
    public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
      //省略
    }

    ZuulProxyAutoConfiguration 继承了 ZuulServerAutoConfiguration ,我们先看下这个配置类

    @Configuration
    @EnableConfigurationProperties({ ZuulProperties.class })
    @ConditionalOnClass({ZuulServlet.class, ZuulServletFilter.class})
    @ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
    // Make sure to get the ServerProperties from the same place as a normal web app would
    // FIXME @Import(ServerPropertiesAutoConfiguration.class)
    public class ZuulServerAutoConfiguration {
    
        @Autowired
        protected ZuulProperties zuulProperties;
    
        @Autowired
        protected ServerProperties server;
    
        @Autowired(required = false)
        private ErrorController errorController;
    
        private Map<String, CorsConfiguration> corsConfigurations;
    
        @Autowired(required = false)
        private List<WebMvcConfigurer> configurers = emptyList();
    
        @Bean
        public HasFeatures zuulFeature() {
            return HasFeatures.namedFeature("Zuul (Simple)", ZuulServerAutoConfiguration.class);
        }
    
        @Bean
        @Primary
        public CompositeRouteLocator primaryRouteLocator(
                Collection<RouteLocator> routeLocators) {
            return new CompositeRouteLocator(routeLocators);
        }
    
        @Bean
        @ConditionalOnMissingBean(SimpleRouteLocator.class)
        public SimpleRouteLocator simpleRouteLocator() {
            return new SimpleRouteLocator(this.server.getServlet().getContextPath(),
                    this.zuulProperties);
        }
    
        @Bean
        public ZuulController zuulController() {
            return new ZuulController();
        }
    
        @Bean
        public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
            ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
            mapping.setErrorController(this.errorController);
            mapping.setCorsConfigurations(getCorsConfigurations());
            return mapping;
        }
    
        protected final Map<String, CorsConfiguration> getCorsConfigurations() {
            if (this.corsConfigurations == null) {
                ZuulCorsRegistry registry = new ZuulCorsRegistry();
                this.configurers
                        .forEach(configurer -> configurer.addCorsMappings(registry));
                this.corsConfigurations = registry.getCorsConfigurations();
            }
            return this.corsConfigurations;
        }
    
        @Bean
        public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
            return new ZuulRefreshListener();
        }
    
        @Bean
        @ConditionalOnMissingBean(name = "zuulServlet")
        @ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false", matchIfMissing = true)
        public ServletRegistrationBean zuulServlet() {
            ServletRegistrationBean<ZuulServlet> 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;
        }
    
        @Bean
        @ConditionalOnMissingBean(name = "zuulServletFilter")
        @ConditionalOnProperty(name = "zuul.use-filter", havingValue = "true", matchIfMissing = false)
        public FilterRegistrationBean zuulServletFilter(){
            final FilterRegistrationBean<ZuulServletFilter> filterRegistration = new FilterRegistrationBean<>();
            filterRegistration.setUrlPatterns(Collections.singleton(this.zuulProperties.getServletPattern()));
            filterRegistration.setFilter(new ZuulServletFilter());
            filterRegistration.setOrder(Ordered.LOWEST_PRECEDENCE);
            // The whole point of exposing this servlet is to provide a route that doesn't
            // buffer requests.
            filterRegistration.addInitParameter("buffer-requests", "false");
            return filterRegistration;
        }
    
        // pre filters
    
        @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 filters
    
        @Bean
        public SendResponseFilter sendResponseFilter(ZuulProperties properties) {
            return new SendResponseFilter(zuulProperties);
        }
    
        @Bean
        public SendErrorFilter sendErrorFilter() {
            return new SendErrorFilter();
        }
    
        @Bean
        public SendForwardFilter sendForwardFilter() {
            return new SendForwardFilter();
        }
    
        @Bean
        @ConditionalOnProperty(value = "zuul.ribbon.eager-load.enabled")
        public ZuulRouteApplicationContextInitializer zuulRoutesApplicationContextInitiazer(
                SpringClientFactory springClientFactory) {
            return new ZuulRouteApplicationContextInitializer(springClientFactory,
                    zuulProperties);
        }
    
        @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);
            }
    
        }
    
        @Configuration
        @ConditionalOnClass(MeterRegistry.class)
        protected static class ZuulCounterFactoryConfiguration {
    
            @Bean
            @ConditionalOnBean(MeterRegistry.class)
            @ConditionalOnMissingBean(CounterFactory.class)
            public CounterFactory counterFactory(MeterRegistry meterRegistry) {
                return new DefaultCounterFactory(meterRegistry);
            }
        }
    
        @Configuration
        protected static class ZuulMetricsConfiguration {
    
            @Bean
            @ConditionalOnMissingClass("io.micrometer.core.instrument.MeterRegistry")
            @ConditionalOnMissingBean(CounterFactory.class)
            public CounterFactory counterFactory() {
                return new EmptyCounterFactory();
            }
    
            @ConditionalOnMissingBean(TracerFactory.class)
            @Bean
            public TracerFactory tracerFactory() {
                return new EmptyTracerFactory();
            }
    
        }
    
        private static class ZuulRefreshListener
                implements ApplicationListener<ApplicationEvent> {
    
            @Autowired
            private ZuulHandlerMapping zuulHandlerMapping;
    
            private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();
    
            @Override
            public void onApplicationEvent(ApplicationEvent event) {
                if (event instanceof ContextRefreshedEvent
                        || event instanceof RefreshScopeRefreshedEvent
                        || event instanceof RoutesRefreshedEvent
                        || event instanceof InstanceRegisteredEvent) {
                    reset();
                }
                else if (event instanceof ParentHeartbeatEvent) {
                    ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
                    resetIfNeeded(e.getValue());
                }
                else if (event instanceof HeartbeatEvent) {
                    HeartbeatEvent e = (HeartbeatEvent) event;
                    resetIfNeeded(e.getValue());
                }
            }
    
            private void resetIfNeeded(Object value) {
                if (this.heartbeatMonitor.update(value)) {
                    reset();
                }
            }
    
            private void reset() {
                this.zuulHandlerMapping.setDirty(true);
            }
        }
    
        private static class ZuulCorsRegistry extends CorsRegistry {
    
            @Override
            protected Map<String, CorsConfiguration> getCorsConfigurations() {
                return super.getCorsConfigurations();
            }
        }
    }

    CompositeRouteLocator :组合路由定位器,看入参就知道应该是会保存好多个RouteLocator,构造过程中其实仅包括一个DiscoveryClientRouteLocator。

    SimpleRouteLocator :默认的路由定位器,主要负责维护配置文件中的路由配置。

    ZuulController :Zuul创建的一个Controller,用于将请求交由ZuulServlet处理。

    ZuulHandlerMapping :这个会添加到SpringMvc的HandlerMapping链中,只有选择了ZuulHandlerMapping的请求才能出发到Zuul的后续流程。

    注册 ZuulFilterInitializer,通过FilterLoader加载应用中所有的过滤器并将过滤器注册到FilterRegistry,那我们接下来一起看下过滤器是如何被加载到应用中的。

    public class ZuulFilterInitializer {
    
        private static final Log log = LogFactory.getLog(ZuulFilterInitializer.class);
    
        private final Map<String, ZuulFilter> filters;
        private final CounterFactory counterFactory;
        private final TracerFactory tracerFactory;
        private final FilterLoader filterLoader;
        private final FilterRegistry filterRegistry;
    
        public ZuulFilterInitializer(Map<String, ZuulFilter> filters,
                                     CounterFactory counterFactory,
                                     TracerFactory tracerFactory,
                                     FilterLoader filterLoader,
                                     FilterRegistry filterRegistry) {
            this.filters = filters;
            this.counterFactory = counterFactory;
            this.tracerFactory = tracerFactory;
            this.filterLoader = filterLoader;
            this.filterRegistry = filterRegistry;
        }
    
        @PostConstruct
        public void contextInitialized() {
            log.info("Starting filter initializer");
    
            TracerFactory.initialize(tracerFactory);
            CounterFactory.initialize(counterFactory);
    
            for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
                filterRegistry.put(entry.getKey(), entry.getValue());
            }
        }
    
        @PreDestroy
        public void contextDestroyed() {
            log.info("Stopping filter initializer");
            for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
                filterRegistry.remove(entry.getKey());
            }
            clearLoaderCache();
    
            TracerFactory.initialize(null);
            CounterFactory.initialize(null);
        }
    
        private void clearLoaderCache() {
            Field field = ReflectionUtils.findField(FilterLoader.class, "hashFiltersByType");
            ReflectionUtils.makeAccessible(field);
            @SuppressWarnings("rawtypes")
            Map cache = (Map) ReflectionUtils.getField(field, filterLoader);
            cache.clear();
        }
    
    }

    ( 2)请求转发
    在Zuul的自动配置中我们看到了 ZuulHandlerMapping ,为SpringMVC中 HandlerMapping 的拓展实
    现,会自动的添加到HandlerMapping链中。

    public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {
      private final RouteLocator routeLocator;
      private final ZuulController zuul;
      private ErrorController errorController;
      private PathMatcher pathMatcher = new AntPathMatcher();
      private volatile boolean dirty = true;
      public ZuulHandlerMapping(RouteLocator routeLocator, ZuulController zuul) {
        this.routeLocator = routeLocator;
        this.zuul = zuul;
        this.setOrder(-200);
     }
      private void registerHandlers() {
        Collection<Route> routes = this.routeLocator.getRoutes();
        if (routes.isEmpty()) {
          this.logger.warn("No routes found from RouteLocator");
       } else {
          Iterator var2 = routes.iterator();
          while(var2.hasNext()) {
            Route route = (Route)var2.next();
            this.registerHandler(route.getFullPath(), this.zuul);
         }
       }
     }
    }

    其主要目的就是把所有路径的请求导入到 ZuulController上.另外的功效是当觉察RouteLocator路由表变
    更,则更新自己dirty状态,重新注册所有Route到ZuulController。

    public class ZuulController extends ServletWrappingController {
      public ZuulController() {
        //在这里已经设置了ZuulServlet
        this.setServletClass(ZuulServlet.class);
        this.setServletName("zuul");
        this.setSupportedMethods((String[])null);
     }
      public ModelAndView handleRequest(HttpServletRequest request,
    HttpServletResponse response) throws Exception {
        ModelAndView var3;
        try {
          //在这里面会调用ZuulServlet的service方法
          var3 = super.handleRequestInternal(request, response);
       } finally {
          RequestContext.getCurrentContext().unset();
       }
        return var3;
       }
    }

    在 ZuulController 中的 handleRequest 方法,会调用已经注册的 ZuulServlet 完成业务请求,我们
    进入 ZuulServlet 看下内部是如何处理的

     public void service(ServletRequest servletRequest, ServletResponse
    servletResponse) throws ServletException, IOException {
        try {
          this.init((HttpServletRequest)servletRequest,
    (HttpServletResponse)servletResponse);
          RequestContext context = RequestContext.getCurrentContext();
          context.setZuulEngineRan();
          try {
            this.preRoute();
         } catch (ZuulException var13) {
            this.error(var13);
            this.postRoute();
            return;
         }
          try {
            this.route();
         } catch (ZuulException var12) {
            this.error(var12);
            this.postRoute();
            return;
         }
          try {
            this.postRoute();
         } catch (ZuulException var11) {
            this.error(var11);
         }
       } catch (Throwable var14) {
          this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" +
    var14.getClass().getName()));
       } finally {
          RequestContext.getCurrentContext().unset();
       }
     }

    ( 3)过滤器
    Zuul默认注入的过滤器可以在 spring -cloud-netflix-core.jar 中找到

    Zuul网关存在的问题
    在实际使用中我们会发现直接使用Zuul会存在诸多问题,包括:
    性能问题
    Zuul1x 版本本质上就是一个同步Servlet,采用多线程阻塞模型进行请求转发。简单讲,每来
    一个请求,Servlet容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户
    端这个线程才会被释放返回容器线程池。如果后台服务调用比较耗时,那么这个线程就会被
    阻塞,阻塞期间线程资源被占用,不能干其它事情。我们知道Servlet容器线程池的大小是有
    限制的,当前端请求量大,而后台慢服务比较多时,很容易耗尽容器线程池内的线程,造成
    容器无法接受新的请求。
    不支持任何长连接,如 websocket

  • 相关阅读:
    实践2.2 内核模块编译
    Linux及安全课程——相关链接总结
    20135202闫佳歆--week 9 期中总结
    20135202闫佳歆--week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程--实验及总结
    20135202闫佳歆--week 8 课本第4章学习笔记
    20135202闫佳歆--week 7 深入理解计算机系统第七章--读书笔记
    20135202闫佳歆--week 8 进程的切换和系统的一般执行过程--学习笔记
    20135202闫佳歆--week 7 Linux内核如何装载和启动一个可执行程序--实验及总结
    laravel open_basedir restriction in effect
    workerman wss net::ERR_SSL_PROTOCOL_ERROR
  • 原文地址:https://www.cnblogs.com/dalianpai/p/12287048.html
Copyright © 2011-2022 走看看