前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Zuul源码分析之 网关处理流程

Zuul源码分析之 网关处理流程

原创
作者头像
全栈开发Dream
修改2021-05-25 17:51:20
6880
修改2021-05-25 17:51:20
举报
文章被收录于专栏:全栈开发Dream

Zuul 处理流程

一、spring-cloud-starter-zuul starter

  • 我们先查看spring-cloud-starter-zuul starter包下有什么,这里的重点就是pom.xml文件,ZuulDeprecationWarningAutoConfiguration.java
  • 打开org.springframework.cloud/spring-cloud-starter-zuul/pom.xml ,可以看到是依赖了spring-cloud-starter-netflix-zuul
代码语言:txt
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <parent>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-netflix</artifactId>
  <version>2.2.5.RELEASE</version>
  <relativePath>..</relativePath> <!-- lookup parent from repository -->
 </parent>
 <artifactId>spring-cloud-starter-zuul</artifactId>
 <name>spring-cloud-starter-zuul</name>
 <description>Spring Cloud Starter Zuul (deprecated, please use spring-cloud-starter-netflix-zuul)</description>
 <url>https://projects.spring.io/spring-cloud</url>
 <organization>
  <name>Pivotal Software, Inc.</name>
  <url>https://www.spring.io</url>
 </organization>
 <properties>
  <main.basedir>${basedir}/../..</main.basedir>
 </properties>
 <dependencies>
  <dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
  </dependency>
 </dependencies>
</project>
  • 我们查看spring-cloud-starter-netflix-zuul包
  • 打开pom.xml可以看到依赖了com.netflix.zuul,所以说Spring Cloud Zuul是基于netflix公司的zuul实现的,除此之外还添加了hystrix及ribbon依赖,所以zuul是自带这两个功能的,spring-boot-starter-web依赖可以使应用成为web应用,spring-boot-starter-actuator是监控依赖
代码语言:txt
复制
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix</artifactId>
        <version>2.2.5.RELEASE</version>
    </parent>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    <name>Spring Cloud Starter Netflix Zuul</name>
    <description>Spring Cloud Starter Netflix Zuul</description>
    <url>https://projects.spring.io/spring-cloud</url>
    <organization>
        <name>Pivotal Software, Inc.</name>
        <url>https://www.spring.io</url>
    </organization>
    <properties>
        <main.basedir>${basedir}/../../..</main.basedir>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-archaius</artifactId>
        </dependency>
        <dependency>
            <groupId>com.netflix.zuul</groupId>
            <artifactId>zuul-core</artifactId>
        </dependency>
    </dependencies>
</project>
  • /META-INF/spring.provides 依赖spring-platform-netflix-core模块及zuul-core模块 1 provides: spring-platform-netflix-core, zuul-core
  • 现在我们进入spring-platform-netflix-core,看看Spring是怎样集成Netflix的一系列框架了,下面是代码框架图
  • 可以看到这个JAR包也包含了spring.factories文件,所以SpringBoot项目启动的时候会检索此配置文件,此文件是zuul实现自动注册配置的关键,下面可以看到熟悉的zuul,hystrix,feign,ribbon的自动配置类
代码语言:txt
复制
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration,\
org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.netflix.feign.FeignAutoConfiguration,\
org.springframework.cloud.netflix.feign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.netflix.feign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration,\
org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration,\
org.springframework.cloud.netflix.rx.RxJavaAutoConfiguration,\
org.springframework.cloud.netflix.metrics.servo.ServoMetricsAutoConfiguration,\
org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration,\
org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration

org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration

org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.netflix.metrics.ServoEnvironmentPostProcessor
  • 我们现在关心Zuul的自动配置类,从上面spring.factories文件可以看到和Zuul相关的是自动配置了两个类,下图可以看到这两个有继承关系,ZuulProxyAutoConfiguration功能最为完全
  • ZuulServerAutoConfiguration 与 ZuulProxyAutoConfiguration
  • ZuulServerAutoConfiguration自动配置类,启动类上如果有@EnableZuulServer则此类生效 1.下面代码可以看到大量使用了@Conditional作为条件判断,注意这个ZuulController这个Bean,它是Zuul的请求入口,这个类实现了Controller了,说明这里也使用了Spring MVC DispatcherServlet, 2.同时此类注册了大量的ZuulFilter 3.代码:
代码语言:txt
复制
/**
 * @author
 */
@Configuration // 声明是配置类
@EnableConfigurationProperties({ ZuulProperties.class }) // 激活 zuul配置
@ConditionalOnClass(ZuulServlet.class) // 条件1 存在ZuulServlet.class
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class) // 条件2 存在ZuulServerMarkerConfiguration.Marker.class bean, 即应用使用@EnableZuulServer注解
// Make sure to get the ServerProperties from the same place as a normal web app would
@Import(ServerPropertiesAutoConfiguration.class) // 配置ServerProperties实例
public class ZuulServerAutoConfiguration {

    @Autowired
    protected ZuulProperties zuulProperties;

    @Autowired
    protected ServerProperties server;

    @Autowired(required = false)
    private ErrorController errorController;

    @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.getServletPrefix(),
                this.zuulProperties);
    }

    /**
     * zuulController, 包装了一个ZuulServlet类型的servlet, 实现对ZuulServlet类型的servlet的初始化.
     *
     * @return
     */
    @Bean
    public ZuulController zuulController() {
        return new ZuulController();
    }

    @Bean
    public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
        ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
        mapping.setErrorController(this.errorController);
        return mapping;
    }

    @Bean
    public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
        return new ZuulRefreshListener();
    }

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

    // 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() {
        return new SendResponseFilter();
    }

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

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

    @Bean
    @ConditionalOnProperty(value = "zuul.ribbon.eager-load.enabled", matchIfMissing = false)
    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(CounterService.class)
    protected static class ZuulCounterFactoryConfiguration {

        @Bean
        @ConditionalOnBean(CounterService.class)
        public CounterFactory counterFactory(CounterService counterService) {
            return new DefaultCounterFactory(counterService);
        }
    }

    @Configuration
    protected static class ZuulMetricsConfiguration {

        @Bean
        @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) {
                this.zuulHandlerMapping.setDirty(true);
            }
            else if (event instanceof HeartbeatEvent) {
                if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {
                    this.zuulHandlerMapping.setDirty(true);
                }
            }
        }

    }

}
  • ZuulProxyAutoConfiguration自动配置类,启动类上如果有对应@EnableZuulProxy则此类生效
  • 由上面此类的继承图可以发现这个类继承了ZuulServerAutoConfiguration,所以此类拥有ZuulServerAutoConfiguration的所有功能,并在此基础上添加了使用了服务发现作为路由寻址功能
  • 代码:
代码语言:txt
复制
/**
 * @author
 */
@Configuration // 声明是配置类
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class, // 引入RibbonCommandFactory配置
        RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
        RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
        HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class) // 条件2 存在ZuulProxyMarkerConfiguration.Marker.class bean, 即应用使用@EnableZuulProxy注解
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {

    @SuppressWarnings("rawtypes")
    @Autowired(required = false)
    private List<RibbonRequestCustomizer> requestCustomizers = Collections.emptyList();

    /**
     * 网关服务注册实例信息
     */
    @Autowired(required = false)
    private Registration registration;

    /**
     * 服务发现客户端
     */
    @Autowired
    private DiscoveryClient discovery;

    /**
     * serviceId和路由的映射逻辑
     */
    @Autowired
    private ServiceRouteMapper serviceRouteMapper;

    @Override
    public HasFeatures zuulFeature() {
        return HasFeatures.namedFeature("Zuul (Discovery)",
                ZuulProxyAutoConfiguration.class);
    }

    /**
     * 静态和动态路由寻址: 静态从配置文件获取, 动态通过服务发现客户端完成. 后者优先级更高
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
    public DiscoveryClientRouteLocator discoveryRouteLocator() {
        return new DiscoveryClientRouteLocator(this.server.getServletPrefix(),
                this.discovery, this.zuulProperties, this.serviceRouteMapper, this.registration);
    }

    // pre filters
    @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
    @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
    public ApplicationListener<ApplicationEvent> zuulDiscoveryRefreshRoutesListener() {
        return new ZuulDiscoveryRefreshListener();
    }

    @Bean
    @ConditionalOnMissingBean(ServiceRouteMapper.class)
    public ServiceRouteMapper serviceRouteMapper() {
        return new SimpleServiceRouteMapper();
    }

    @Configuration
    @ConditionalOnMissingClass("org.springframework.boot.actuate.endpoint.Endpoint")
    protected static class NoActuatorConfiguration {

        @Bean
        public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
            ProxyRequestHelper helper = new ProxyRequestHelper();
            helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders());
            helper.setTraceRequestBody(zuulProperties.isTraceRequestBody());
            return helper;
        }

    }

    /**
     * 添加 Endpoint
     */
    @Configuration
    @ConditionalOnClass(Endpoint.class)
    protected static class EndpointConfiguration {

        @Autowired(required = false)
        private TraceRepository traces;

        @ConditionalOnEnabledEndpoint("routes")
        @Bean
        public RoutesEndpoint routesEndpoint(RouteLocator routeLocator) {
            return new RoutesEndpoint(routeLocator);
        }

        @ConditionalOnEnabledEndpoint("routes")
        @Bean
        public RoutesMvcEndpoint routesMvcEndpoint(RouteLocator routeLocator,
                RoutesEndpoint endpoint) {
            return new RoutesMvcEndpoint(endpoint, routeLocator);
        }

        @ConditionalOnEnabledEndpoint("filters")
        @Bean
        public FiltersEndpoint filtersEndpoint() {
            FilterRegistry filterRegistry = FilterRegistry.instance();
            return new FiltersEndpoint(filterRegistry);
        }

        @Bean
        public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
            TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
            if (this.traces != null) {
                helper.setTraces(this.traces);
            }
            helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders());
            helper.setTraceRequestBody(zuulProperties.isTraceRequestBody());
            return helper;
        }
    }

    private static class ZuulDiscoveryRefreshListener
            implements ApplicationListener<ApplicationEvent> {

        private HeartbeatMonitor monitor = new HeartbeatMonitor();

        @Autowired
        private ZuulHandlerMapping zuulHandlerMapping;

        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (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.monitor.update(value)) {
                reset();
            }
        }

        private void reset() {
            this.zuulHandlerMapping.setDirty(true);
        }

    }

}
  • ZuulServerAutoConfiguration 与 ZuulProxyAutoConfiguration具体使用哪种模式,是分别通过@EnableZuulServer 和@EnableZuulProxy注解来区别的
  • 前者使用了ZuulProperties进行配置路由寻址;
  • 后者在原来的基础上添加了使用了服务发现作为路由寻址功能, 并使用Ribbon做客户端的负载均衡,这个最为常用;

二、@EnableZuulProxy

  • @EnableZuulProxy注解
代码语言:txt
复制
/**
 * 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
 *
 * @author
 */
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
}
  • @EnableZuulProxy分析
  • @EnableCircuitBreaker注解用于开启短路器功能
代码语言:txt
复制
/**
 * Annotation to enable a CircuitBreaker implementation.
 * http://martinfowler.com/bliki/CircuitBreaker.html
 * @author
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableCircuitBreakerImportSelector.class)
public @interface EnableCircuitBreaker {

}
  • @Import(ZuulProxyMarkerConfiguration.class)
  • ZuulProxyMarkerConfiguration.Marker.class
代码语言:txt
复制
@Configuration
public class ZuulProxyMarkerConfiguration {
    @Bean
    public Marker zuulProxyMarkerBean() {
        return new Marker();
    }

    class Marker {
    }
}

三、使用Consul作为注册中心

  • @EnableZuulProxy模式下的zuul需要注册中心的支持,因为eureka已经被抛弃了,我这里选用的是Consul1.添加Maven依赖
代码语言:txt
复制
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

2.启动类上加上@EnableDiscoveryClient注解

代码语言:txt
复制
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class ZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }

}

3.通过以上所有步骤后,整个服务网关Zuul应用就可以发挥作用了

四、总结

1.Spring Cloud对Netflix Zuul做了封装集成, 使得在Spring Cloud环境中使用Zuul更方便,只需添加spring-cloud-starter-zuul maven依赖及启动类上添加@EnableZuulProxy就可创建一个zuul应用。

2.Spring Cloud Zuul 实际上就是在Servlet的基础上添加了一些ZuulFilter去完成一些额外事情,封装了就成框架了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Zuul 处理流程
    • 一、spring-cloud-starter-zuul starter
      • 二、@EnableZuulProxy
        • 三、使用Consul作为注册中心
          • 2.启动类上加上@EnableDiscoveryClient注解
          • 3.通过以上所有步骤后,整个服务网关Zuul应用就可以发挥作用了
        • 四、总结
        相关产品与服务
        API 网关
        腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档