十八.SpringCloud源码剖析-Zuul的自动配置和核心Filter详解

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: 在上一章节我们大致了解了Zuul的Filter的执行流程和核心的Filter,这一章节我们消息分析一下Zuul的自动配置,以及每个Filter的实现细节,这需要你有一定的耐心

前言

在上一章节我们大致了解了Zuul的Filter的执行流程和核心的Filter,这一章节我们消息分析一下Zuul的自动配置,以及每个Filter的实现细节,这需要你有一定的耐心

<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

一.Zuul的自动配置

1.EnableZuulProxy 开启zuul服务

我们在使用Zuul的时候需要在启动类贴上@EnableZuulProxy注解,我们就从这个注解入手分析Zuul,首先打开注解的源码看一下

/**
 * Sets up a Zuul server endpoint and installs some reverse proxy filters in it, so it can
 * forward requests to backend servers. The backends can be registered manually through
 * configuration or via DiscoveryClient.
 *
 * @see EnableZuulServer for how to get a Zuul server without any proxying
 */
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
   
   
}

EnableZuulProxy的注释告诉我们,这里设设置Zuul服务器端点,和安装了一些反向代理过滤器,通过这些过滤器它可以转发请求到后端服务器,可以通过配置或通过DiscoveryClient手动注册后端服务(服务发现)

不过这里@Import 导入了一个配类 ZuulProxyMarkerConfiguration,这个配置在干嘛呢?看一下源码

/**
  负责添加标记bean以触发 {@link ZuulProxyAutoConfiguration}的激活
 * Responsible for adding in a marker bean to trigger activation of 
 * {@link ZuulProxyAutoConfiguration}
 *
 * @author Biju Kunjummen
 */

@Configuration
public class ZuulProxyMarkerConfiguration {
   
   
    @Bean
    public Marker zuulProxyMarkerBean() {
   
   
        return new Marker();
    }

    class Marker {
   
   
    }
}

翻译:“Responsible for adding in a marker bean to trigger activation of

  • {@link ZuulProxyAutoConfiguration}”
    这个类的作用就是用来激活 ZuulProxyAutoConfiguration Zuul的自动配置的

它在负责添加标记bean以触发激活 ZuulProxyAutoConfiguration 这个类,研究过springboot自动配置的同学就会知道 ,SpringBoot 中会有大量的 xxxAutoConfiguration 自动配置的类会在应用启动的过程中被激活实现自动装配,从而节省了我们很多的配置

2.Zuul自动配置ZuulProxyAutoConfiguration

ZuulProxyAutoConfiguration是zuul比较核心的一个自动配置类,很多的组件都是在这个类中注册的,该自动配置类在 spring-cloud-netflix-zuul.xx.RELEASE 包的spring.factories文件中有定义,随着SpringBoot启动该配置类被注册到Spring容器中(不理解的可以先了解一下SpringBoot自动配置)
在这里插入图片描述
自动配置类 ZuulProxyAutoConfiguration 需要通过ZuulProxyMarkerConfiguration 来激活才可能被注册到容器中,下面是它的源码

@Configuration
//这里引入了几种客户端配置
@Import({
   
    RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
        RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
        RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
        HttpClientConfiguration.class })
//注册Bean的条件,必须有 ZuulProxyMarkerConfiguration.Marker 的实例
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
   
   
...省略...

3.Zuul自动配置ZuulServerAutoConfiguration

ZuulProxyAutoConfiguration继承了ZuulServerAutoConfiguration ,我们先跟一下它父类

/**
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Biju Kunjummen
 */
@Configuration
//开启配置:ZuulProperties ,这里面是以 zuul 打头的配置
@EnableConfigurationProperties({
   
    ZuulProperties.class })
@ConditionalOnClass(ZuulServlet.class)
//必须满足有 ZuulServerMarkerConfiguration.Marker 的实例才能被注册
@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 {
   
   
  //绑定zuul的配置信息,这里面是以 zuul 打头的配置
    @Autowired
    protected ZuulProperties zuulProperties;
    //以server打头的服务器配置
    @Autowired
    protected ServerProperties server;

    //注入请求错误的控制器,主要返回错误页面的路径
    @Autowired(required = false)
    private ErrorController errorController;

    @Bean
    public HasFeatures zuulFeature() {
   
   
        return HasFeatures.namedFeature("Zuul (Simple)", ZuulServerAutoConfiguration.class);
    }

    //RouteLocator that composes multiple RouteLocators. :
    //多路由组合定位器
    @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().getServletPrefix(),
                this.zuulProperties);
    }

    //Zuul的请求入口控制器
    @Bean
    public ZuulController zuulController() {
   
   
        return new ZuulController();
    }

    // MVC HandlerMapping that maps incoming request paths to remote services.
    //他是做请求路径和远程服务的映射的,是 HandlerMapping的实现
    @Bean
    public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
   
   
        ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
        mapping.setErrorController(this.errorController);
        return mapping;
    }

    //定义ZuulRefreshListener  zuul刷新的监听器
    @Bean
    public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
   
   
        return new ZuulRefreshListener();
    }

    // Core Zuul servlet which intializes and orchestrates zuulFilter execution
    //这里在注册ZuulServlet 这样的一个servlet, 这个东西了不得了,
    //他是负责核心ZuulServlet初始化和调用zuulFilter执行,跟DispatcherServlet差不多
    @Bean
    @ConditionalOnMissingBean(name = "zuulServlet")
    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;
    }

    // pre filters  : 
    //在pre过滤器中,它是第一个执行的过滤器,检测请求是用 DispatcherServlet还是 ZuulServlet,将结果设置到RequestContext中
    @Bean
    public ServletDetectionFilter servletDetectionFilter() {
   
   
        return new ServletDetectionFilter();
    }
    //前置过滤器,用于解析表单数据并将其重新编码,只是针对于表单数据,即:content-type中必须带有“application/x-www-form-urlencoded”或“multipart/form-data”
    @Bean
    public FormBodyWrapperFilter formBodyWrapperFilter() {
   
   
        return new FormBodyWrapperFilter();
    }

    //设置请求过程是否开启debug,将当前请求上下文中的debugRouting和debugRequest参数设置为true
    @Bean
    public DebugFilter debugFilter() {
   
   
        return new DebugFilter();
    }
    //主要是将原始请求进行包装,将原始的HttpServletRequest请求包装成Servlet30RequestWrapper类型
    @Bean
    public Servlet30WrapperFilter servlet30WrapperFilter() {
   
   
        return new Servlet30WrapperFilter();
    }

    // post filters
    //下面是定义一系列的后置过滤器,SendResponseFilter是Zuul的最后一个Filter,负责最终响应结果的输出
    @Bean
    public SendResponseFilter sendResponseFilter(ZuulProperties properties) {
   
   
        return new SendResponseFilter(zuulProperties);
    }
    //这个是用来发送错误的Filter
    @Bean
    public SendErrorFilter sendErrorFilter() {
   
   
        return new SendErrorFilter();
    }
    //它使用RequestDispatcher转发请求
    @Bean
    public SendForwardFilter sendForwardFilter() {
   
   
        return new SendForwardFilter();
    }

整理一下ZuulServerAutoConfiguration 配置类里面做的事情

  • 1.注册了多路由组合定位器 CompositeRouteLocator,它是由多个RouteLocator组成的 ,路由定位器就是根据请求的path来定位对应的微服务的。
  • 2.注册了简单的路由定位器SimpleRouteLocator

  • 3.注册了很多内置的FiIlter,包括前置FiIlter,后置FiIlter,路由FiIlter,异常FiIlter

  • 4.注册了ZuulHandlerMapping 是对path和远程服务的映射

  • 5.注册了zuulServlet : 请求的分发器类似于DispatcherServlet

  • 6.注册了ZuulControllerZuulServlet会通过调用其父类ServletWrappingController的service再实现对请求的调用

    4.Zuul自动配置ZuulProxyAutoConfiguration

    我们看了父类 ZuulServerAutoConfiguration 大概知道了里面配置的内容,回到ZuulProxyAutoConfiguration看一下它做了哪些配置

    /**
    * @author Spencer Gibb
    * @author Dave Syer
    * @author Biju Kunjummen
    */
    @Configuration
    @Import({
         
          RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
          RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
          RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
          HttpClientConfiguration.class })
    //配置激活条件,必须有ZuulProxyMarkerConfiguration.Marker的实例
    @ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
    public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
         
         
      ...省略代码...
      //服务发现的路由定位器,它将静态的配置RouteLocator路由与DiscoveryClient路由组合在一起, 通过服务发现的方式进行路由
      @Bean
      @ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
      public DiscoveryClientRouteLocator discoveryRouteLocator() {
         
         
          return new DiscoveryClientRouteLocator(this.server.getServlet().getServletPrefix(), this.discovery, this.zuulProperties,
                  this.serviceRouteMapper, this.registration);
      }
    
      // pre filters  : 前置过滤
      //Pre ZuulFilter可以根据提供的RouteLocator确定在哪里以及如何进行路由。 还为下游请求设置各种与代理相关的标头。
      @Bean
      public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
         
         
          return new PreDecorationFilter(routeLocator, this.server.getServlet().getServletPrefix(), this.zuulProperties,
                  proxyRequestHelper);
      }
    
      // route filters   :路由过滤,实现了Ribbon的负载均衡和hystrix
      @Bean
      public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
              RibbonCommandFactory<?> ribbonCommandFactory) {
         
         
          RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory,
                  this.requestCustomizers);
          return filter;
      }
    
      //简单主机的路由Filter
      @Bean
      @ConditionalOnMissingBean({
         
         SimpleHostRoutingFilter.class, CloseableHttpClient.class})
      public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper,
              ZuulProperties zuulProperties,
              ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
              ApacheHttpClientFactory httpClientFactory) {
         
         
          return new SimpleHostRoutingFilter(helper, zuulProperties,
                  connectionManagerFactory, httpClientFactory);
      }
    
      @Bean
      @ConditionalOnMissingBean({
         
         SimpleHostRoutingFilter.class})
      public SimpleHostRoutingFilter simpleHostRoutingFilter2(ProxyRequestHelper helper,
                                                             ZuulProperties zuulProperties,
                                                             CloseableHttpClient httpClient) {
         
         
          return new SimpleHostRoutingFilter(helper, zuulProperties,
                  httpClient);
      }
      //服务名和路由组件的映射器,提供一种在路由和发现的服务名称之间应用约定的方法
      @Bean
      @ConditionalOnMissingBean(ServiceRouteMapper.class)
      public ServiceRouteMapper serviceRouteMapper() {
         
         
          return new SimpleServiceRouteMapper();
      }
    

    在 ZuulProxyAutoConfiguration 中做的事情比较简单,就是定义了一堆Filter,包括:

  • DiscoveryClientRouteLocator : 静态路由定位器,通过服务发现的方式找到要调用的服务
    • PreDecorationFilter :Pre Filter可以根据提供的RouteLocator确定在哪里以及如何进行路由。 还为下游请求设置各种与代理相关的标头。
    • RibbonRoutingFilter :route Filter:路由过滤,实现了Ribbon的负载均衡和hystrix
    • SimpleHostRoutingFilter : 简单主机的路由Filter,它通过HttpClient将请求发送到预定的URL, URL可在RequestContext.getRouteHost()中找到。

二.Zuul的核心组件

1.ZuulController请求入口控制器

ZuulController还是挺重要的,它是zuul的请求入口控制器,它创建了ZuulServlet去执行请求,ZuulController的源码:

public class ZuulController extends ServletWrappingController {
   
   

    public ZuulController() {
   
   
        //把 ZuulServlet 设置给父类
        setServletClass(ZuulServlet.class);
        setServletName("zuul");
        setSupportedMethods((String[]) null); // Allow all
    }

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
   
        try {
   
   
            // We don't care about the other features of the base class, just want to
            // handle the request
            //执行父类的请求方法
            return super.handleRequestInternal(request, response);
        }
        finally {
   
   
            // @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter
            RequestContext.getCurrentContext().unset();
        }
    }

}

父类 ServletWrappingController 源码:

public class ServletWrappingController extends AbstractController implements BeanNameAware, InitializingBean, DisposableBean {
   
   
       ...省略...
    @Nullable
    private Servlet servletInstance;
    //反射创建 ZuulServlet和初始化
      public void afterPropertiesSet() throws Exception {
   
   
        if (this.servletClass == null) {
   
   
            throw new IllegalArgumentException("'servletClass' is required");
        } else {
   
   
            if (this.servletName == null) {
   
   
                this.servletName = this.beanName;
            }
            //反射创建 ZuulServlet和初始化
            this.servletInstance = (Servlet)ReflectionUtils.accessibleConstructor(this.servletClass, new Class[0]).newInstance();
            this.servletInstance.init(new ServletWrappingController.DelegatingServletConfig());
        }
    }
   //核心方法,调用 servletInstance 执行请求
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
   
        Assert.state(this.servletInstance != null, "No Servlet instance");
        //调用 ZuulServlet执行请求
        this.servletInstance.service(request, response);
        return null;
    }

2.RouteLocator路由定位器

RouteLocator是路由定位器,就是根据请求的path来定位对应的微服务的 ,在ZuulServerAutoConfiguration 中定义了 CompositeRouteLocator 多路由组合定位器 和 SimpleRouteLocator 简单路由定位器,在ZuulProxyAutoConfiguration中定义了 DiscoveryClientRouteLocator ,我们看一下 继承体系
在这里插入图片描述
RouteLocator 是路由定位器接口,提供了

  • getIgnoredPaths:获取忽略的服务paths集合,
  • getRoutes :获取服务路由Route集合 ,这里的Route是对yml中 zuul.routes的封装
  • getMatchingRoute :根据path获取路由Route
    ```java
    public interface RouteLocator {

    /**

    • Ignored route paths (or patterns), if any.
      */
      Collection getIgnoredPaths();

      /**

    • A map of route path (pattern) to location (e.g. service id or URL).
      */
      List getRoutes();

      /**

    • Maps a path to an actual route with full metadata.
      */
      Route getMatchingRoute(String path);

}

`CompositeRouteLocator` 是多路由组合定位器,它主要是调用多个路由定位器根据path找到Route
```java
public class CompositeRouteLocator implements RefreshableRouteLocator {
    //省略
    @Override
    public Route getMatchingRoute(String path) {
        //遍历所有的 routeLocators
        for (RouteLocator locator : routeLocators) {
            //通过 path找到 Route
            Route route = locator.getMatchingRoute(path);
            if (route != null) {
                return route;
            }
        }
        return null;
    }

}

SimpleRouteLocator 是最简单的路由定位器,根据path找到route返回

protected Route getSimpleMatchingRoute(final String path) {
   
   
        if (log.isDebugEnabled()) {
   
   
            log.debug("Finding route for path: " + path);
        }
        //获取所有的路由
        // This is called for the initialization done in getRoutesMap()
        getRoutesMap();

        if (log.isDebugEnabled()) {
   
   
            log.debug("servletPath=" + this.dispatcherServletPath);
            log.debug("zuulServletPath=" + this.zuulServletPath);
            log.debug("RequestUtils.isDispatcherServletRequest()="
                    + RequestUtils.isDispatcherServletRequest());
            log.debug("RequestUtils.isZuulServletRequest()="
                    + RequestUtils.isZuulServletRequest());
        }
        //调整路径
        String adjustedPath = adjustPath(path);
        //根据Path查找Route
        ZuulRoute route = getZuulRoute(adjustedPath);

        return getRoute(route, adjustedPath);
    }
    //根据path匹配Route
    protected ZuulRoute getZuulRoute(String adjustedPath) {
   
   
        //不忽略
        if (!matchesIgnoredPatterns(adjustedPath)) {
   
   
            //遍历所有的route
            for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) {
   
   
                String pattern = entry.getKey();
                log.debug("Matching pattern:" + pattern);
                //根据path简单匹配 route,返回route
                if (this.pathMatcher.match(pattern, adjustedPath)) {
   
   
                    return entry.getValue();
                }
            }
        }
        return null;
    }

重点看一下 DiscoveryClientRouteLocator ,配置RouteLocator路由与DiscoveryClient路由组合在一起,优先使用服务发现方式,源码

/**
 * A {@link RouteLocator} that combines static, configured routes with those from a
 * {@link DiscoveryClient}. The discovery client takes precedence.
 *
 * @author Spencer Gibb
 * @author Dave Syer
 */
public class DiscoveryClientRouteLocator extends SimpleRouteLocator
        implements RefreshableRouteLocator {
   
   

    private static final Log log = LogFactory.getLog(DiscoveryClientRouteLocator.class);

    public static final String DEFAULT_ROUTE = "/**";
    //服务发现客户端,用来从注册中心找服务的
    private DiscoveryClient discovery;

    //zuul的配置,以zuul开头的配置项
    private ZuulProperties properties;

    //提供一种在 路由 和 发现的服务名称 之间应用约定的方法
    private ServiceRouteMapper serviceRouteMapper;
    //初始化
    public DiscoveryClientRouteLocator(String servletPath, DiscoveryClient discovery,
            ZuulProperties properties, ServiceInstance localServiceInstance) {
   
   
        super(servletPath, properties);
        //添加忽略的服务,对应配置:ignored-services: "*"
        if (properties.isIgnoreLocalService() && localServiceInstance != null) {
   
   
            String localServiceId = localServiceInstance.getServiceId();
            if (!properties.getIgnoredServices().contains(localServiceId)) {
   
   
                properties.getIgnoredServices().add(localServiceId);
            }
        }
        this.serviceRouteMapper = new SimpleServiceRouteMapper();
        //初始化服务发现客户端
        this.discovery = discovery;
        this.properties = properties;
    }
    //定位路由,读取yml配置的zuul.routes中的路由,然后把需要忽略的忽略掉,加上前缀返回。
    @Override
    protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
   
   
        LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
        //拿到所有的routes配置添加到map中,对应yml中的zuul.routes配置
        routesMap.putAll(super.locateRoutes());
        if (this.discovery != null) {
   
   
            Map<String, ZuulRoute> staticServices = new LinkedHashMap<>();
            for (ZuulRoute route : routesMap.values()) {
   
   
                //取到配置的每个路由的微服务的ID
                String serviceId = route.getServiceId();
                if (serviceId == null) {
   
   
                    serviceId = route.getId();
                }
                if (serviceId != null) {
   
   
                    //以 服务ID为key把每个路由配置放到staticServices中
                    staticServices.put(serviceId, route);
                }
            }
            // Add routes for discovery services by default
            //通过服务发现客户端找到服务注册表中的服务
            List<String> services = this.discovery.getServices();
            //得到配置中需要忽略的服务的服务名
            String[] ignored = this.properties.getIgnoredServices()
                    .toArray(new String[0]);
            //循环从注册表中找到的服务 , 这里要处理忽略的服务
            for (String serviceId : services) {
   
   
                // Ignore specifically ignored services and those that were manually
                // configured

                String key = "/" + mapRouteToService(serviceId) + "/**";
                if (staticServices.containsKey(serviceId)
                        && staticServices.get(serviceId).getUrl() == null) {
   
   
                    // Explicitly configured with no URL, cannot be ignored
                    // all static routes are already in routesMap
                    // Update location using serviceId if location is null
                    ZuulRoute staticRoute = staticServices.get(serviceId);
                    if (!StringUtils.hasText(staticRoute.getLocation())) {
   
   
                        staticRoute.setLocation(serviceId);
                    }
                }
                //通过工具判断哪些服务要忽略,不忽略的加入routesMap中
                if (!PatternMatchUtils.simpleMatch(ignored, serviceId)
                        && !routesMap.containsKey(key)) {
   
   
                    // Not ignored
                    routesMap.put(key, new ZuulRoute(key, serviceId));
                }
            }
        }
        if (routesMap.get(DEFAULT_ROUTE) != null) {
   
   
            ZuulRoute defaultRoute = routesMap.get(DEFAULT_ROUTE);
            // Move the defaultServiceId to the end
            routesMap.remove(DEFAULT_ROUTE);
            routesMap.put(DEFAULT_ROUTE, defaultRoute);
        }
        LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
        //处理路径的 前缀
        for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
   
   
            String path = entry.getKey();
            // Prepend with slash if not already present.
            if (!path.startsWith("/")) {
   
   
                path = "/" + path;
            }
            if (StringUtils.hasText(this.properties.getPrefix())) {
   
   
                path = this.properties.getPrefix() + path;
                //如果没有前缀就加上前缀
                if (!path.startsWith("/")) {
   
   
                    path = "/" + path;
                }
            }
            values.put(path, entry.getValue());
        }
        return values;
    }

3.Zuul的内置Filter详解

ServletDetectionFilter

前置通知 ,执行顺序 -3 , 该Filter是用来标记该请求是通过 DispatcherServlet处理还是通过 ZuulServlet处理run()把判断结果以boolean值的方式保存到HttpServletRequest中,后续的处理中就可以通过它获取到这个标记做不同的处理,而这个filter执行的顺序是 -3(filterOrder() 方法) ,越小越先执行

/**
 * Detects whether a request is ran through the {@link DispatcherServlet} or {@link ZuulServlet}.
 * The purpose was to detect this up-front at the very beginning of Zuul filter processing
 *  and rely on this information in all filters.
 *  RequestContext is used such that the information is accessible to classes 
 *  which do not have a request reference.
 * @author Adrian Ivan
 */
public class ServletDetectionFilter extends ZuulFilter {
   
   

    public ServletDetectionFilter() {
   
   
    }
    //前置通知
    @Override
    public String filterType() {
   
   
        return PRE_TYPE;
    }

    /**
     * Must run before other filters that rely on the difference between 
     * DispatcherServlet and ZuulServlet.
     */
    //filterOrder 决定了这个过滤器的执行顺序 这里是 :-3 见
    //public static final int SERVLET_DETECTION_FILTER_ORDER = -3;
    @Override
    public int filterOrder() {
   
   
        return SERVLET_DETECTION_FILTER_ORDER;
    }

    @Override
    public boolean shouldFilter() {
   
   
        return true; 
    }

    @Override
    public Object run() {
   
   
        RequestContext ctx = RequestContext.getCurrentContext();
        //判断结果保存到  HttpServletRequest中
        HttpServletRequest request = ctx.getRequest();
        //如果请求中设置了DispatcherServletRequest属性就给RequestContext 设置 IS_DISPATCHER_SERVLET_REQUEST_KEY = true
        if (!(request instanceof HttpServletRequestWrapper) 
                && isDispatcherServletRequest(request)) {
   
   
            //上下文设置:isDispatcherServletRequest = true
            ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, true);
        } else {
   
   
            //上下文设置:isDispatcherServletRequest = false
            ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, false);
        }

        return null;
    }

    //判断当前请求是否是DispatcherServletRequest
    private boolean isDispatcherServletRequest(HttpServletRequest request) {
   
   
        return request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null;
    }
FormBodyWrapperFilter

前置通知,执行顺序 -1 , 解析表单数据并为后续处理重新编码,由于后续的请求中,将符合要求的请求体包装成FormBodyRequestWrapper对象。

/**
 * Pre {@link ZuulFilter} that parses form data and reencodes it for downstream services
 *
 * @author Dave Syer
 */
public class FormBodyWrapperFilter extends ZuulFilter {
   
   
    ...省略...
    @Override
    public String filterType() {
   
   
        //前置Filter
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
   
   
        //执行顺序 -1
        return FORM_BODY_WRAPPER_FILTER_ORDER;
    }
    @Override
    public Object run() {
   
   
        RequestContext ctx = RequestContext.getCurrentContext();
        //处理请求
        HttpServletRequest request = ctx.getRequest();
        FormBodyRequestWrapper wrapper = null;
        if (request instanceof HttpServletRequestWrapper) {
   
   
            HttpServletRequest wrapped = (HttpServletRequest) ReflectionUtils
                    .getField(this.requestField, request);
            //包装成 FormBodyRequestWrapper
            wrapper = new FormBodyRequestWrapper(wrapped);
            ReflectionUtils.setField(this.requestField, request, wrapper);
            if (request instanceof ServletRequestWrapper) {
   
   
                ReflectionUtils.setField(this.servletRequestField, request, wrapper);
            }
        }
        else {
   
   
            //包装成 FormBodyRequestWrapper
            wrapper = new FormBodyRequestWrapper(request);
            ctx.setRequest(wrapper);
        }
        if (wrapper != null) {
   
   
            ctx.getZuulRequestHeaders().put("content-type", wrapper.getContentType());
        }
        return null;
    }
...省略...
DebugFilter

开启调试标记,如果请求中设置了“debug”请求参数, RequestContext调试属性设置为true。说白了就是通过 reques中的debug参数来激活调试信息,这样当线上环境出现问题的时候,可以通过请求参数的方式来激活这些debug信息以帮助分析问题,前置通知 ,执行顺序 1

/**
 * Pre {@link ZuulFilter} that sets {@link RequestContext} debug attributes to true if
 * the "debug" request parameter is set.
 *
 * @author Spencer Gibb
 */
public class DebugFilter extends ZuulFilter {
   
   

    private static final DynamicBooleanProperty ROUTING_DEBUG = DynamicPropertyFactory
            .getInstance().getBooleanProperty(ZuulConstants.ZUUL_DEBUG_REQUEST, false);

    private static final DynamicStringProperty DEBUG_PARAMETER = DynamicPropertyFactory
            .getInstance().getStringProperty(ZuulConstants.ZUUL_DEBUG_PARAMETER, "debug");

    @Override
    public String filterType() {
   
   
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
   
   
        return DEBUG_FILTER_ORDER;
    }

    @Override
    public boolean shouldFilter() {
   
   
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        if ("true".equals(request.getParameter(DEBUG_PARAMETER.get()))) {
   
   
            return true;
        }
        return ROUTING_DEBUG.get();
    }
    @Override
    public Object run() {
   
   
        RequestContext ctx = RequestContext.getCurrentContext();
        //设置上下文,开启Routing和Request的debug功能
        ctx.setDebugRouting(true);
        ctx.setDebugRequest(true);
        return null;
    }
Servlet30WrapperFilter

这里是对原始的HttpServletRequest请求包装成Servlet30RequestWrapper对象即要兼容3.0。zuul默认只是兼容2.5,前置通知 ,执行顺序 -2


/**
 * Pre {@link ZuulFilter} that wraps requests in a Servlet 3.0 compliant wrapper.
 * Zuul's default wrapper is only Servlet 2.5 compliant.
 * @author Spencer Gibb
 */
public class Servlet30WrapperFilter extends ZuulFilter {
   
   

    private Field requestField = null;

    public Servlet30WrapperFilter() {
   
   
        this.requestField = ReflectionUtils.findField(HttpServletRequestWrapper.class,
                "req", HttpServletRequest.class);
        Assert.notNull(this.requestField,
                "HttpServletRequestWrapper.req field not found");
        this.requestField.setAccessible(true);
    }

    protected Field getRequestField() {
   
   
        return this.requestField;
    }

    @Override
    public String filterType() {
   
   
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
   
   
        return SERVLET_30_WRAPPER_FILTER_ORDER;
    }
    @Override
    public Object run() {
   
   
    //把请求包装成  Servlet30RequestWrapper
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        if (request instanceof HttpServletRequestWrapper) {
   
   
            request = (HttpServletRequest) ReflectionUtils.getField(this.requestField,
                    request);
            ctx.setRequest(new Servlet30RequestWrapper(request));
        }
        else if (RequestUtils.isDispatcherServletRequest()) {
   
   
            // If it's going through the dispatcher we need to buffer the body

            ctx.setRequest(new Servlet30RequestWrapper(request));
        }
        return null;
    }

你现在知道为什么他叫 Servlet30WrapperFilter 了吗?

SendResponseFilter

后置通知 ,处理请求响应,执行顺序 1000

/**
 * Post {@link ZuulFilter} that writes responses from proxied requests to the current response.
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Ryan Baxter
 */
public class SendResponseFilter extends ZuulFilter {
   
   
...省略...

    @Override
    public Object run() {
   
   
        try {
   
   
            //添加响应头
            addResponseHeaders();
            //添加响应的内容
            writeResponse();
        }
        catch (Exception ex) {
   
   
            ReflectionUtils.rethrowRuntimeException(ex);
        }
        return null;
    }


    private void writeResponse() throws Exception {
   
   
        RequestContext context = RequestContext.getCurrentContext();
        // there is no body to send
        if (context.getResponseBody() == null
                && context.getResponseDataStream() == null) {
   
   
            return;
        }
        HttpServletResponse servletResponse = context.getResponse();
        if (servletResponse.getCharacterEncoding() == null) {
   
    // only set if not set
            servletResponse.setCharacterEncoding("UTF-8");
        }
        OutputStream outStream = servletResponse.getOutputStream();
        InputStream is = null;
        try {
   
   
            if (RequestContext.getCurrentContext().getResponseBody() != null) {
   
   
                String body = RequestContext.getCurrentContext().getResponseBody();
                writeResponse(
                        new ByteArrayInputStream(
                                body.getBytes(servletResponse.getCharacterEncoding())),
                        outStream);
                return;
            }
        ...省略...


    //写响应结果
    private void writeResponse(InputStream zin, OutputStream out) throws Exception {
   
   
        byte[] bytes = buffers.get();
        int bytesRead = -1;
        while ((bytesRead = zin.read(bytes)) != -1) {
   
   
            out.write(bytes, 0, bytesRead);
        }
    }

翻译大致意思为把代理请求的响应写入到当前响应,String body = RequestContext.getCurrentContext().getResponseBody(); 获取到响应内容 ,通过 servletResponse.getOutputStream(); 写出去 ,
我们从源码中可以看到该过滤器会检查请求上下文中是否包含请求响应相关的头信息、响应数据流或是响应体,然后利用请求上下文的响应信息来组织需要发送回客户端的响应内容。

SendErrorFilter

错误处理过滤器 ,把错误重定向到/error路径上,执行顺序 0


/**
 * Error {@link ZuulFilter} that forwards to /error (by default) if {@link RequestContext#getThrowable()} is not null.
 *
 * @author Spencer Gibb
 */
//TODO: move to error package in Edgware
public class SendErrorFilter extends ZuulFilter {
   
   

    private static final Log log = LogFactory.getLog(SendErrorFilter.class);
    protected static final String SEND_ERROR_FILTER_RAN = "sendErrorFilter.ran";

    //异常重定向路径
    @Value("${error.path:/error}")
    private String errorPath;

    @Override
    public String filterType() {
   
   
        return ERROR_TYPE;
    }

    @Override
    public int filterOrder() {
   
   
        return SEND_ERROR_FILTER_ORDER;
    }

    @Override
    public boolean shouldFilter() {
   
   
        RequestContext ctx = RequestContext.getCurrentContext();
        // only forward to errorPath if it hasn't been forwarded to already
        return ctx.getThrowable() != null
                && !ctx.getBoolean(SEND_ERROR_FILTER_RAN, false);
    }

    @Override
    public Object run() {
   
   
        try {
   
   
            RequestContext ctx = RequestContext.getCurrentContext();
            //找到异常
            ZuulException exception = findZuulException(ctx.getThrowable());
            HttpServletRequest request = ctx.getRequest();

            //处理异常错误码等
            request.setAttribute("javax.servlet.error.status_code", exception.nStatusCode);

            log.warn("Error during filtering", exception);
            request.setAttribute("javax.servlet.error.exception", exception);

            if (StringUtils.hasText(exception.errorCause)) {
   
   
                request.setAttribute("javax.servlet.error.message", exception.errorCause);
            }

            RequestDispatcher dispatcher = request.getRequestDispatcher(
                    this.errorPath);
            if (dispatcher != null) {
   
   
                ctx.set(SEND_ERROR_FILTER_RAN, true);
                if (!ctx.getResponse().isCommitted()) {
   
   
                    ctx.setResponseStatusCode(exception.nStatusCode);
                    dispatcher.forward(request, ctx.getResponse());
                }
            }
        }
        catch (Exception ex) {
   
   
            ReflectionUtils.rethrowRuntimeException(ex);
        }
        return null;
    }
SendForwardFilter

用来处理路由规则中的forward本地跳转配置 ,执行顺序 5000

/**
 * Route {@link ZuulFilter} that forwards requests using the {@link RequestDispatcher}.
 * Forwarding location is located in the {@link RequestContext} attribute {@link org.springframework.cloud.netflix.zuul.filters.support.FilterConstants#FORWARD_TO_KEY}.
 * Useful for forwarding to endpoints in the current application.
 用户RequestDispatcher 进行本地应用端点的Forwarding
 * @author Dave Syer
 */
public class SendForwardFilter extends ZuulFilter {
   
   
    ...省略...
    @Override
    public Object run() {
   
   
        try {
   
   
            RequestContext ctx = RequestContext.getCurrentContext();
            String path = (String) ctx.get(FORWARD_TO_KEY);
            RequestDispatcher dispatcher = ctx.getRequest().getRequestDispatcher(path);
            if (dispatcher != null) {
   
   
                ctx.set(SEND_FORWARD_FILTER_RAN, true);
                if (!ctx.getResponse().isCommitted()) {
   
   
                    //请求跳转
                    dispatcher.forward(ctx.getRequest(), ctx.getResponse());
                    ctx.getResponse().flushBuffer();
                }
            }
        }
        catch (Exception ex) {
   
   
            ReflectionUtils.rethrowRuntimeException(ex);
        }
        return null;
    }

下面是 ZuulProxyAutoConfiguration 中还定义了一些过滤器

PreDecorationFilter

Pre 前置filter可以根据提供的RouteLocator确定在哪里以及如何进行路由。 还为下游请求设置各种与代理相关的请求头,执行顺序 5

/**
 * Pre {@link ZuulFilter} that determines where and how to route based on the supplied {@link RouteLocator}.
 * Also sets various proxy related headers for downstream requests.
 */
public class PreDecorationFilter extends ZuulFilter {
   
   
    ...省略...
    @Override
    public Object run() {
   
   
        //请求上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        //请求路径
        final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
        //根据请求地址,匹配匹配路由(url对应要访问的服务)
        Route route = 
        this.routeLocator.getMatchingRoute(requestURI);
        if (route != null) {
   
   
            //从路由中获取请求服务id
            String location = route.getLocation();
            if (location != null) {
   
   
                //设置请求上下文相关信息
                //请求的资源路径  path
                ctx.put(REQUEST_URI_KEY, route.getPath());
                //路由的id
                ctx.put(PROXY_KEY, route.getId());
                if (!route.isCustomSensitiveHeaders()) {
   
   
                    //忽略敏感的请求头
                    this.proxyRequestHelper
                            .addIgnoredHeaders(this.properties.getSensitiveHeaders().toArray(new String[0]));
                }
                else {
   
   
                    this.proxyRequestHelper.addIgnoredHeaders(route.getSensitiveHeaders().toArray(new String[0]));
                }
                if (route.getRetryable() != null) {
   
   
                    //可否重试
                    ctx.put(RETRYABLE_KEY, route.getRetryable());
                }

                if (location.startsWith(HTTP_SCHEME+":") || location.startsWith(HTTPS_SCHEME+":")) {
   
   
                    ctx.setRouteHost(getUrl(location));
                    ctx.addOriginResponseHeader(SERVICE_HEADER, location);
                }
                else if (location.startsWith(FORWARD_LOCATION_PREFIX)) {
   
   
                    ctx.set(FORWARD_TO_KEY,
                            StringUtils.cleanPath(location.substring(FORWARD_LOCATION_PREFIX.length()) + route.getPath()));
                    ctx.setRouteHost(null);
                    return null;
                }
                else {
   
   
                      //设置服务id绑定到上下文个,在RibbonReques中使用
                    // set serviceId for use in filters.route.RibbonRequest
                    ctx.set(SERVICE_ID_KEY, location);
                    ctx.setRouteHost(null);
                    ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location);
                }
                //添加代理请求头
                if (this.properties.isAddProxyHeaders()) {
   
   
                    addProxyHeaders(ctx, route);
                    String xforwardedfor = ctx.getRequest().getHeader(X_FORWARDED_FOR_HEADER);
                    String remoteAddr = ctx.getRequest().getRemoteAddr();
                    if (xforwardedfor == null) {
   
   
                        xforwardedfor = remoteAddr;
                    }
                    else if (!xforwardedfor.contains(remoteAddr)) {
   
    // Prevent duplicates
                        xforwardedfor += ", " + remoteAddr;
                    }
                    ctx.addZuulRequestHeader(X_FORWARDED_FOR_HEADER, xforwardedfor);
                }
                if (this.properties.isAddHostHeader()) {
   
   
                    ctx.addZuulRequestHeader(HttpHeaders.HOST, toHostHeader(ctx.getRequest()));
                }

...省略...
RibbonRoutingFilter

Routing过滤器,使用Ribbon和Hystrix来向服务实例发起请求 ,有服务熔断机制,执行顺序 10


/**
 * Route {@link ZuulFilter} that uses Ribbon, Hystrix and pluggable http clients to send requests.
 * ServiceIds are found in the {@link RequestContext} attribute {@link org.springframework.cloud.netflix.zuul.filters.support.FilterConstants#SERVICE_ID_KEY}.
通过  Ribbon 和  Hystrix 向http客户端发送请求
通过 RequestContext找到  ServiceIds服务id ,


 * @author Spencer Gibb
 * @author Dave Syer
 * @author Ryan Baxter
 */
public class RibbonRoutingFilter extends ZuulFilter {
   
   
...省略...

@Override
    public Object run() {
   
   
        //获取请求上下文 
        RequestContext context = RequestContext.getCurrentContext();
        this.helper.addIgnoredHeaders();
        try {
   
   

            //创建一个 RibbonCommandContext Ribbon命令上下文,用来发请求
            RibbonCommandContext commandContext = buildCommandContext(context);
            //发送请求,获取到结果
            ClientHttpResponse response = forward(commandContext);
            setResponse(response);
            return response;
        }
        catch (ZuulException ex) {
   
   
            throw new ZuulRuntimeException(ex);
        }
        catch (Exception ex) {
   
   
            throw new ZuulRuntimeException(ex);
        }
    }

    //根据RequestContext 请求上下文,获取请求服务id,url等封装成RibbonCommandContext
    protected RibbonCommandContext buildCommandContext(RequestContext context) {
   
   
        HttpServletRequest request = context.getRequest();

        MultiValueMap<String, String> headers = this.helper
                .buildZuulRequestHeaders(request);
        MultiValueMap<String, String> params = this.helper
                .buildZuulRequestQueryParams(request);
        String verb = getVerb(request);
        InputStream requestEntity = getRequestBody(request);
        if (request.getContentLength() < 0 && !verb.equalsIgnoreCase("GET")) {
   
   
            context.setChunkedRequestBody();
        }

        String serviceId = (String) context.get(SERVICE_ID_KEY);
        Boolean retryable = (Boolean) context.get(RETRYABLE_KEY);
        Object loadBalancerKey = context.get(LOAD_BALANCER_KEY);

        String uri = this.helper.buildZuulRequestURI(request);

        // remove double slashes
        uri = uri.replace("//", "/");

        long contentLength = useServlet31 ? request.getContentLengthLong(): request.getContentLength();

        return new RibbonCommandContext(serviceId, verb, uri, retryable, headers, params,
                requestEntity, this.requestCustomizers, contentLength, loadBalancerKey);
    }

    protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
   
   
        Map<String, Object> info = this.helper.debug(context.getMethod(),
                context.getUri(), context.getHeaders(), context.getParams(),
                context.getRequestEntity());

        RibbonCommand command = this.ribbonCommandFactory.create(context);
        try {
   
   
            //执行请求
            ClientHttpResponse response = command.execute();
            this.helper.appendDebug(info, response.getRawStatusCode(), response.getHeaders());
            return response;
        }
        catch (HystrixRuntimeException ex) {
   
   
            //处理异常
            return handleException(info, ex);
        }

    }
    //处理异常
    protected ClientHttpResponse handleException(Map<String, Object> info,
            HystrixRuntimeException ex) throws ZuulException {
   
   
        int statusCode = HttpStatus.INTERNAL_SERVER_ERROR.value();
        Throwable cause = ex;
        String message = ex.getFailureType().toString();

        ClientException clientException = findClientException(ex);
        if (clientException == null) {
   
   
            clientException = findClientException(ex.getFallbackException());
        }

        if (clientException != null) {
   
   
            if (clientException
                    .getErrorType() == ClientException.ErrorType.SERVER_THROTTLED) {
   
   
                statusCode = HttpStatus.SERVICE_UNAVAILABLE.value();
            }
            cause = clientException;
            message = clientException.getErrorType().toString();
        }
        info.put("status", String.valueOf(statusCode));
        throw new ZuulException(cause, "Forwarding error", statusCode, message);
    }
SimpleHostRoutingFilter

通过RequestContext#getRouteHost()找到调用的服务地址 ,使用http客户端实现调用 ,他和RibbonRoutingFilter的区别是没有使用Hystrix所以并没有线程隔离和断路器的保护。
执行顺序 100

/**
 * Route {@link ZuulFilter} that sends requests to predetermined URLs via apache
 * {@link HttpClient}. URLs are found in {@link RequestContext#getRouteHost()}.

通过RequestContext#getRouteHost()找到调用的服务地址 ,使用apache的http客户端实现调用
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Bilal Alp
 * @author Gang Li
 */
public class SimpleHostRoutingFilter extends ZuulFilter {
   
   

...省略...
@Override
    public Object run() {
   
   
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        MultiValueMap<String, String> headers = this.helper
                .buildZuulRequestHeaders(request);
        MultiValueMap<String, String> params = this.helper
                .buildZuulRequestQueryParams(request);
        String verb = getVerb(request);
        InputStream requestEntity = getRequestBody(request);
        if (request.getContentLength() < 0) {
   
   
            context.setChunkedRequestBody();
        }

        //获取请求地址
        String uri = this.helper.buildZuulRequestURI(request);
        this.helper.addIgnoredHeaders();

        try {
   
   
            //发送请求
            CloseableHttpResponse response = forward(this.httpClient, verb, uri, request,
                    headers, params, requestEntity);
            setResponse(response);
        }
        catch (Exception ex) {
   
   
            throw new ZuulRuntimeException(ex);
        }
        return null;
    }
        ------------------------------------
    //使用 httpclient 发送请求
    private CloseableHttpResponse forward(CloseableHttpClient httpclient, String verb,
            String uri, HttpServletRequest request, MultiValueMap<String, String> headers,
            MultiValueMap<String, String> params, InputStream requestEntity)
            throws Exception {
   
   
        Map<String, Object> info = this.helper.debug(verb, uri, headers, params,
                requestEntity);
    //请求主机
        URL host = RequestContext.getCurrentContext().getRouteHost();
        HttpHost httpHost = getHttpHost(host);
    //请求地址
        uri = StringUtils.cleanPath((host.getPath() + uri).replaceAll("/{2,}", "/"));
        int contentLength = request.getContentLength();

        ContentType contentType = null;

        if (request.getContentType() != null) {
   
   
            contentType = ContentType.parse(request.getContentType());
        }

        InputStreamEntity entity = new InputStreamEntity(requestEntity, contentLength,
                contentType);

        HttpRequest httpRequest = buildHttpRequest(verb, uri, entity, headers, params,
                request);
        try {
   
   
            log.debug(httpHost.getHostName() + " " + httpHost.getPort() + " "
                    + httpHost.getSchemeName());
            //发送请求
            CloseableHttpResponse zuulResponse = forwardRequest(httpclient, httpHost,
                    httpRequest);
            this.helper.appendDebug(info, zuulResponse.getStatusLine().getStatusCode(),
                    revertHeaders(zuulResponse.getAllHeaders()));
            return zuulResponse;
        }
        finally {
   
   
            // When HttpClient instance is no longer needed,
            // shut down the connection manager to ensure
            // immediate deallocation of all system resources
            // httpclient.getConnectionManager().shutdown();
        }
    }
------------------------------------
        protected HttpRequest buildHttpRequest(String verb, String uri,
            InputStreamEntity entity, MultiValueMap<String, String> headers,
            MultiValueMap<String, String> params, HttpServletRequest request) {
   
   
        HttpRequest httpRequest;
        String uriWithQueryString = uri + (this.forceOriginalQueryStringEncoding
                ? getEncodedQueryString(request) : 

        this.helper.getQueryString(params));
        //处理各种请求方式
        switch (verb.toUpperCase()) {
   
   
        case "POST":
            HttpPost httpPost = new HttpPost(uriWithQueryString);
            httpRequest = httpPost;
            httpPost.setEntity(entity);
            break;
        case "PUT":
            HttpPut httpPut = new HttpPut(uriWithQueryString);
            httpRequest = httpPut;
            httpPut.setEntity(entity);
            break;
        case "PATCH":
            HttpPatch httpPatch = new HttpPatch(uriWithQueryString);
            httpRequest = httpPatch;
            httpPatch.setEntity(entity);
            break;
        case "DELETE":
            BasicHttpEntityEnclosingRequest entityRequest = new BasicHttpEntityEnclosingRequest(
                    verb, uriWithQueryString);
            httpRequest = entityRequest;
            entityRequest.setEntity(entity);
            break;
        default:
            httpRequest = new BasicHttpRequest(verb, uriWithQueryString);
            log.debug(uriWithQueryString);
        }

        httpRequest.setHeaders(convertHeaders(headers));
        return httpRequest;
    }
------------------------------------
    //最终执行请求
    private CloseableHttpResponse forwardRequest(CloseableHttpClient httpclient,
            HttpHost httpHost, HttpRequest httpRequest) throws IOException {
   
   
        return httpclient.execute(httpHost, httpRequest);
    }

到这里我们把 ZuulProxyAutoConfiguration 自动配置类中定义的比较重要的一些过滤器都介绍完了 ,zuul在执行过程中就会按照这些filter的调用顺序去执行,我们来用表格整理一下

类型 过滤器 描述 顺序
pre ServletDetectionFilter 在pre过滤器中,ServletDetectionFilter是第一个执行的过滤器,检测请求是用 DispatcherServlet还是 ZuulServlet,将结果设置到RequestContext中 -3
pre Servlet30WrapperFilter 主要是将原始请求进行包装,将原始的HttpServletRequest请求包装成Servlet30RequestWrapper类型 -2
pre FormBodyWrapperFilter 同Servlet30RequestWrapper一样,也是对请求的一个包装,只不过他只包装表单数据,即:content-type中必须带有“application/x-www-form-urlencoded”或“multipart/form-data” -1
error SendErrorFilter 这个是用来发送错误的Filter 0
pre DebugFilter 设置请求过程是否开启debug,将当前请求上下文中的debugRoutingdebugRequest参数设置为true 1
pre PreDecorationFilter 基本的路由转发配置,根据uri调用哪一个route过滤器 5
route RibbonRoutingFilter 服务路由的过滤器,使用用Ribbon 做负载均衡,hystrix做熔断 10
route SimpleHostRoutingFilter 简单主机路由过滤器,如果使用url路由,则用这个过滤器 100
route SendForwardFilter 它使用RequestDispatcher转发请求 500
post SendResponseFilter SendResponseFilter是Zuul的最后一个Filter,负责最终响应结果的输出。 1000

那这一章我们分析到这里 ,下一章我们跟踪一下zuul的执行流程,看他是如果把这些filter串联起来的 。

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
2月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
417 37
|
2月前
|
负载均衡 Java Nacos
SpringCloud基础2——Nacos配置、Feign、Gateway
nacos配置管理、Feign远程调用、Gateway服务网关
SpringCloud基础2——Nacos配置、Feign、Gateway
|
2月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
106 5
|
3月前
|
人工智能 前端开发 Java
【实操】Spring Cloud Alibaba AI,阿里AI这不得玩一下(含前后端源码)
本文介绍了如何使用 **Spring Cloud Alibaba AI** 构建基于 Spring Boot 和 uni-app 的聊天机器人应用。主要内容包括:Spring Cloud Alibaba AI 的概念与功能,使用前的准备工作(如 JDK 17+、Spring Boot 3.0+ 及通义 API-KEY),详细实操步骤(涵盖前后端开发工具、组件选择、功能分析及关键代码示例)。最终展示了如何成功实现具备基本聊天功能的 AI 应用,帮助读者快速搭建智能聊天系统并探索更多高级功能。
1360 2
【实操】Spring Cloud Alibaba AI,阿里AI这不得玩一下(含前后端源码)
|
1月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
37 0
|
3月前
|
Java 微服务 Spring
Spring Cloud全解析:配置中心之解决configserver单点问题
但是如果该configserver挂掉了,那就无法获取最新的配置了,微服务就出现了configserver的单点问题,那么如何避免configserver单点呢?
|
3月前
|
运维 Java Nacos
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
|
3月前
|
Java 数据库连接 Nacos
SpringCloud微服务配置管理、配置热更新
SpringCloud微服务配置管理、配置热更新
109 0
|
2月前
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba
|
3月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
126 1
下一篇
无影云桌面