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

原文地址:https://www.cnblogs.com/dalianpai/p/12287048.html

时间: 2024-11-06 03:53:06

Zuul的核心源码解析的相关文章

java并发之线程池Executor 核心源码解析

1.什么是线程池 定义:线程池是指管理一组同构工作线程的资源池 组成部分: 线程管理器(ThreadPool):用于创建并管理线程池.包括创建线程池,销毁线程池,添加新任务 工作线程(PoolWorker):线程池中的线程 任务接口(Task):每个任务必须实现的接口,一共工作线程调度任务的执行 任务队列:用于存放没有处理的任务,提供一种缓冲机制 2.为什么要使用线程池 通过重用现有的线程而不是创建新线程,从而减少了线程创建 和 销毁过程中的巨大开销 当请求到达时,工作线程已经存在,不用再等待线

spark内核揭秘-05-SparkContext核心源码解析初体验

SparkContext在获得了一系列的初始化信息后开始创建并启动TaskScheduler实例: 进入createTaskScheduler方法: 我们看一下其Standalone的方式: 在上述代码中首先实例化一个TaskSchedulerImpl: 然后构建出了masterUrls: 接着创建出关键的backend: 进入SparkDeploySchedulerBackend实现: 从以上截图可以看出来,SparkDeploySchedulerBackend核心是为了启动CoarseGra

1小时手写SpringMVC T5大牛带你解读Spring核心源码(附详细视频教程)

SpringMVC简介 SpringMVC是当前最优秀的MVC框架,自从Spring 2.5版本发布后,由于支持注解配置,易用性有了大幅度的提高.Spring 3.0更加完善,实现了对Struts 2的超越.现在越来越多的开发团队选择了Spring MVC. Spring为展现层提供的基于MVC设计理念的优秀的Web框架,是目前最主流的MVC框架之一 Spring3.0后全面超越Struts2,成为最优秀的MVC框架 Spring MVC通过一套MVC注解,让POJO成为处理请求的控制器,而无须

Backbone事件机制核心源码(仅包含Events、Model模块)

一.应用场景 为了改善酷版139邮箱的代码结构,引入backbone的事件机制,按照MVC的分层思想搭建酷版云邮局的代码框架.力求在保持酷版轻量级的基础上提高代码的可维护性. 二.遗留问题 1.backbone的升级问题,新的特性无法引入 2.backbone中的潜在BUG,若官方已修复则无法同步更新 解决办法: 关注backbone官网的更新记录. http://github.com/documentcloud/backbone/ 三.核心源码 /** * @裁剪版backbone,仅包含Ev

1 手写ArrayList核心源码

手写ArrayList核心源码 ArrayList是Java中常用的数据结构,不光有ArrayList,还有LinkedList,HashMap,LinkedHashMap,HashSet,Queue,PriorityQueue等等,我们将手写这些常用的数据结构的核心源码,用尽量少的代码来揭示核心原理. 下面我们来手写ArrayList的核心源码 首先我们定义一个QArrayList,不要问为什么叫QArrayList,因为我之前写过Qt,仅此而已.源码 public class<T> QAr

TreeMap核心源码实现解析

TreeMap实现了SotredMap接口,它是有序的集合.而且是一个红黑树结构,每个key-value都作为一个红黑树的节点.如果在调用TreeMap的构造函数时没有指定比较器,则根据key执行自然排序,如果指定了比较器则按照比较器来进行排序. 一.数据结构 1.继承关系 public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io

Java HashMap 核心源码解读

本篇对HashMap实现的源码进行简单的分析. 所使用的HashMap源码的版本信息如下: /* * @(#)HashMap.java 1.73 07/03/13 * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ 一.概述 在Java中每一个对象都有一个哈希码,这个值可以通过hashCo

迷你版jQuery——zepto核心源码分析

前言 zepto号称迷你版jQuery,并且成为移动端dom操作库的首选 事实上zepto很多时候只是借用了jQuery的名气,保持了与其基本一致的API,其内部实现早已面目全非! 艾伦分析了jQuery,小钗暂时没有那个本事分析jQuery,这里就恬不知耻说说自己对zepto的源码理解,希望对各位有用 首先zepto的出现其实还是很讨巧的,他看见了巨人jQuery在移动浪潮来临时的转身慢.牵挂多的问题 马上搞出了一套轻量级类jQuery框架代码,核心代码1000行不到,快速占领了移动端的市场,

JMeter5.0核心源码浅析[转]

[转自:https://blog.csdn.net/zuozewei/article/details/85042829] 源码下载地址:https://github.com/apache/jmeter废话不多说,下面进入正题~ 一.源码结构 1. 工程目录 2. 源码目录 3. 源码分析 运行机制 HashTree 是 JMeter 执行测试依赖的数据结构,在执行测试之前进行配置测试数据,HashTree将数据组织到一个递归树结构中,并提供了操作该结构的方法 StandardJMeterEngi