原文链接: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; } }
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; }); }
到这里,我们的路由信息就加载完毕了,网关也就启动完成了,之后就是发送请求过滤器发功的时候了。