spring cloud gateway 启动流程及原理分析


 原文链接:https://blog.csdn.net/qq_37616173/article/details/83790842


 

网关的包结构

 

 

 

actuate中定义了一个叫GatewayControllerEndpoint的类,这个类提供一些对外的接口,可以获取网关的一些信息,比如路由的信息,改变路由地址等等。

config中定义了一些启动时去加载的类,配置路由信息和读取你的配置文件就在这里完成。

discovery中定义了注册中心相关的内容,包括注册中心的路由等。

event定义了一些事件他们都继承自ApplicationEvent,对事件发布不了解的可以去看看spring的代码。

filter中定义了spring cloud gateway实现的一些过滤器。

handler中定义了很多Predicate相关的Factory

route就是我们路由的相关

support是工具包等。

 


 

 

启动流程

网关启动第一步加载的就是去加载config包下的几个类。

这几个类就定义了网关需要加载的配置项。

在我第一次做网关开发的时候我引入了spring-boot-starter-web的依赖,这样是会报错的,因为gateway是基于spring-webflux开发的,他依赖的DispatcherHandler就和我们web里的DispatcherServlet一样的功能。

 

1、加载GatewayClassPathWarningAutoConfiguration这个类,就指明了我们需要什么不需要什么,他加载于GatewayAutoConfiguration之前,如果DispatcherServlet存在,就会给与警告,同样的DispatcherHandler不存在也会警告。

@Configuration
@AutoConfigureBefore(GatewayAutoConfiguration.class)
public class GatewayClassPathWarningAutoConfiguration {

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

    private static final String BORDER = "\n\n**********************************************************\n\n";

    @Configuration
    @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
    protected static class SpringMvcFoundOnClasspathConfiguration {

        public SpringMvcFoundOnClasspathConfiguration() {
            log.warn(BORDER
                    + "Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. "
                    + "Please remove spring-boot-starter-web dependency." + BORDER);
        }

    }

    @Configuration
    @ConditionalOnMissingClass("org.springframework.web.reactive.DispatcherHandler")
    protected static class WebfluxMissingFromClasspathConfiguration {

        public WebfluxMissingFromClasspathConfiguration() {
            log.warn(BORDER + "Spring Webflux is missing from the classpath, "
                    + "which is required for Spring Cloud Gateway at this time. "
                    + "Please add spring-boot-starter-webflux dependency." + BORDER);
        }

    }

}

 

 

2、它加载完成之后加载的是GatewayLoadBalancerClientAutoConfiguration这个是gateway负载均衡的过滤器实现的加载,他将LoadBalancerClientFilter 注入到了容器中,这个过滤器后面再说。

@Configuration
@ConditionalOnClass({ LoadBalancerClient.class, RibbonAutoConfiguration.class,
        DispatcherHandler.class })
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@EnableConfigurationProperties(LoadBalancerProperties.class)
public class GatewayLoadBalancerClientAutoConfiguration {

    // GlobalFilter beans

    @Bean
    @ConditionalOnBean(LoadBalancerClient.class)
    @ConditionalOnMissingBean(LoadBalancerClientFilter.class)
    public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client,
            LoadBalancerProperties properties) {
        return new LoadBalancerClientFilter(client, properties);
    }

}

 

 

3、之后便是我们的GatewayAutoConfiguration正式加载了,这个里面定义了非常多的内容,我们大部分用到的过滤器,过滤器工厂都是在这里构建的。包括之前的gatewayControllerEndpoint也是在这里注入容器中的。这个类的定义很长,我就不再这里都放了,列举几个。

@Configuration

//开启网关,不写默认为true

@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)

@EnableConfigurationProperties

@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)

@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})

@ConditionalOnClass(DispatcherHandler.class)

public class GatewayAutoConfiguration {

 @Configuration

 @ConditionalOnClass(HttpClient.class)

 protected static class NettyConfiguration {

  @Bean

  @ConditionalOnMissingBean

  public HttpClient httpClient(@Qualifier("nettyClientOptions") Consumer<? super HttpClientOptions.Builder> options) {

   return HttpClient.create(options);

  }

  ... //还有很多 这里不列举了。我们都知道spring cloud 是基于Netty实现的,这里他这个静态内部类就是初始化netty需要的东西。

}

//初始化了加载配置文件的对象,建立route

@Bean

 public GatewayProperties gatewayProperties() {

  return new GatewayProperties();

 }

//初始化请求转发 过滤器

@Bean

 @ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled", matchIfMissing = true)

 public ForwardedHeadersFilter forwardedHeadersFilter() {

  return new ForwardedHeadersFilter();

 }

 //最后初始化gatewayControllerEndpoint 这里注意只有引入spring-boot-starter-actuator他才会加载

@Configuration

 @ConditionalOnClass(Health.class)

 protected static class GatewayActuatorConfiguration {

  @Bean

  @ConditionalOnEnabledEndpoint

  public GatewayControllerEndpoint gatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,

                List<GatewayFilterFactory> GatewayFilters, RouteDefinitionWriter routeDefinitionWriter,

                RouteLocator routeLocator) {

   return new GatewayControllerEndpoint(routeDefinitionLocator, globalFilters, GatewayFilters, routeDefinitionWriter, routeLocator);

  }

 }

 

4、我们通过注册中心发现的路由不是在config包下定义的而是在discovery包下GatewayDiscoveryClientAutoConfiguration实现了从注册中心发现内容

@Configuration

@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)

@AutoConfigureBefore(GatewayAutoConfiguration.class)

@ConditionalOnClass({DispatcherHandler.class, DiscoveryClient.class})

@EnableConfigurationProperties

public class GatewayDiscoveryClientAutoConfiguration {

 @Bean

 @ConditionalOnBean(DiscoveryClient.class)

 @ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")

 public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(

   DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {

  return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);

 }

 @Bean public DiscoveryLocatorProperties discoveryLocatorProperties() {

  DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();

  properties.setPredicates(initPredicates());

  properties.setFilters(initFilters());

  return properties;

 }

}
View Code

 

 

5、这些注册完毕后,我们的配置文件就开始读取了,这两个中定义了我们配置文件的读取规则,其中DiscoveryLocatorProperties都有默认的值,我们可以不用关心。GatewayProperties比较重要,这里就定义了我们配置文件里的所有路由信息,读取完成后,接下来就要变成真正的路由信息了。

@ConfigurationProperties("spring.cloud.gateway")
@Validated
public class GatewayProperties {

    private final Log logger = LogFactory.getLog(getClass());

    /**
     * List of Routes.
     */
    @NotNull
    @Valid
    private List<RouteDefinition> routes = new ArrayList<>();

    /**
     * List of filter definitions that are applied to every route.
     */
    private List<FilterDefinition> defaultFilters = new ArrayList<>();

    private List<MediaType> streamingMediaTypes = Arrays
            .asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON);

    public List<RouteDefinition> getRoutes() {
        return routes;
    }

    public void setRoutes(List<RouteDefinition> routes) {
        this.routes = routes;
        if (routes != null && routes.size() > 0 && logger.isDebugEnabled()) {
            logger.debug("Routes supplied from Gateway Properties: " + routes);
        }
    }

    public List<FilterDefinition> getDefaultFilters() {
        return defaultFilters;
    }

    public void setDefaultFilters(List<FilterDefinition> defaultFilters) {
        this.defaultFilters = defaultFilters;
    }

    public List<MediaType> getStreamingMediaTypes() {
        return streamingMediaTypes;
    }

    public void setStreamingMediaTypes(List<MediaType> streamingMediaTypes) {
        this.streamingMediaTypes = streamingMediaTypes;
    }

    @Override
    public String toString() {
        return "GatewayProperties{" + "routes=" + routes + ", defaultFilters="
                + defaultFilters + ", streamingMediaTypes=" + streamingMediaTypes + '}';
    }

}

 

 

这里就要说一下 RouteLocator接口,这个接口提供了一个getRoutes()方法返回一个Flux所以,他的实现中就定义了读取配置文件转成路由的关键。

public interface RouteLocator {

 Flux<Route> getRoutes();

}

 

我们先看从配置文件中加载的路由信息也就是他的实现RouteDefinitionRouteLocator

 

public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {

 protected final Log logger = LogFactory.getLog(getClass());

 private final RouteDefinitionLocator routeDefinitionLocator;

 private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();

 private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();

 private final GatewayProperties gatewayProperties;

 private final SpelExpressionParser parser = new SpelExpressionParser();

 private BeanFactory beanFactory;

 private ApplicationEventPublisher publisher;

 public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,

            List<RoutePredicateFactory> predicates,

            List<GatewayFilterFactory> gatewayFilterFactories,

            GatewayProperties gatewayProperties) {

  this.routeDefinitionLocator = routeDefinitionLocator;

  initFactories(predicates);

  gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));

  this.gatewayProperties = gatewayProperties;

 }

  ...

}

 

这个类的一个属性routeDefinitionLocator中已经定义了我们的路由,只不过是代理对象。 这个属性所对应的接口RouteDefinitionLocator有很多种实现,现在看PropertiesRouteDefinitionLocator,

他的构造方法读取了GatewayProperties ,所以到这里我们的路由就已经存在了。通过他的getRoutes()方法,我们就能方便的取出所有定义的路由信息了

public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {

 private final GatewayProperties properties;

 public PropertiesRouteDefinitionLocator(GatewayProperties properties) {

  this.properties = properties;

 }

 @Override

 public Flux<RouteDefinition> getRouteDefinitions() {

  return Flux.fromIterable(this.properties.getRoutes());

 }

}

 

而 RouteDefinitionRouteLocator 的getRoutes()方法又是这样定义的

    @Override
    public Flux<Route> getRoutes() {
        return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute)
                // TODO: error handling
                .map(route -> {
                    if (logger.isDebugEnabled()) {
                        logger.debug("RouteDefinition matched: " + route.getId());
                    }
                    return route;
                });

        /*
         * TODO: trace logging if (logger.isTraceEnabled()) {
         * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
         */
    }

 

routeDefinitionLocator.getRouteDefinitions()返回从配置文件中读取的路由转换成 Flux 再通过convertToRoute转换成路由对象的数组,封装成Flux()至此,配置文件中读取路由信息就结束了。

接下来我们来解释通过注册中心的方式接受的路由信息。这里由一个叫DiscoveryClientRouteDefinitionLocator的类来实现,他同样实现了RouteDefinitionLocator接口,能够返回Flux,

当我们调用到getRouteDefinitions的时候,他通过discoveryClient转换出了服务发现中心的服务路由,注意这里他没有被转换为Flux而是保留为Flux 这是一个坑,

我们前期很多时候去找Route发现根本就没有从服务注册中心拉下来的,现在才知道,他压根就没去转。

 

public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {

 private final DiscoveryClient discoveryClient;

 private final DiscoveryLocatorProperties properties;

 private final String routeIdPrefix;

 public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {

// 服务发现信息 

  this.discoveryClient = discoveryClient;

 //这个配置是前面说过的DiscoveryLocatorProperties

  this.properties = properties;

  if (StringUtils.hasText(properties.getRouteIdPrefix())) {

   this.routeIdPrefix = properties.getRouteIdPrefix();

  } else {

   this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_";

  }

 }

 //这个是核心

 @Override

 public Flux<RouteDefinition> getRouteDefinitions() {

  SimpleEvaluationContext evalCtxt = SimpleEvaluationContext

    .forReadOnlyDataBinding()

    .withInstanceMethods()

    .build();

  SpelExpressionParser parser = new SpelExpressionParser();

  Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());

  Expression urlExpr = parser.parseExpression(properties.getUrlExpression());

  return Flux.fromIterable(discoveryClient.getServices())

    .map(discoveryClient::getInstances)

    .filter(instances -> !instances.isEmpty())

    .map(instances -> instances.get(0))

    .filter(instance -> {

     Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);

     if (include == null) {

      return false;

     }

     return include;

    })

    .map(instance -> {

     String serviceId = instance.getServiceId();

                    RouteDefinition routeDefinition = new RouteDefinition();

                    routeDefinition.setId(this.routeIdPrefix + serviceId);

     String uri = urlExpr.getValue(evalCtxt, instance, String.class);

     routeDefinition.setUri(URI.create(uri));

     final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);

     for (PredicateDefinition original : this.properties.getPredicates()) {

      PredicateDefinition predicate = new PredicateDefinition();

      predicate.setName(original.getName());

      for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {

       String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);

       predicate.addArg(entry.getKey(), value);

      }

      routeDefinition.getPredicates().add(predicate);

     }

                    for (FilterDefinition original : this.properties.getFilters()) {

                     FilterDefinition filter = new FilterDefinition();

                     filter.setName(original.getName());

      for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {

       String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);

       filter.addArg(entry.getKey(), value);

      }

      routeDefinition.getFilters().add(filter);

     }

                    return routeDefinition;

    });

 }

 

到这里,我们的路由信息就加载完毕了,网关也就启动完成了,之后就是发送请求过滤器发功的时候了。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM