This article mainly reveals the loading of Spring Cloud Gateway and how to handle the request process from the source point of view.
1. Overview of spring gateway
Spring Cloud Gateway is an official gateway developed by spring based on Spring 5.0, Spring Boot 2.0, Project Reactor and other technologies. Spring Cloud Gateway aims to provide a simple and effective unified API route management method for microservice architecture. As a gateway in the Spring Cloud ecosystem, Spring Cloud Gateway aims to replace Netflix ZUUL, which not only provides a unified routing mode, but also provides the basic functions of the gateway based on the Filter chain, such as: security, monitoring / burying point, and current limiting.
2. Simple loading analysis of container startup process
The process of loading is mainly spring containers. You need to be familiar with the spring framework. There is not much analysis here.
Under the org.springframework.cloud.gateway.config package, we can see four configuration classes:
GatewayAutoConfiguration
GatewayClassPathWarningAutoConfiguration
GatewayLoadBalancerClientAutoConfiguration
GatewayRedisAutoConfiguration
Their initialization sequence is as follows:
2.1 GatewayClassPathWarningAutoConfiguration
Spring Cloud Gateway 2.x is implemented based on Spring WebFlux.
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration is used to check whether the project has correctly imported the spring boot starter weblux dependency, rather than the spring boot starter web dependency. Click to view source code
2.2 GatewayLoadBalancerClientAutoConfiguration
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration, initialize LoadBalancerClientFilter, click View source code
2.3 GatewayRedisAutoConfiguration
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration to initialize RedisRateLimiter.
RequestRateLimiterGatewayFilterFactory implements the current limiting function of gateway based on RedisRateLimiter
2.4 GatewayAutoConfiguration
org.springframework.cloud.gateway.config.GatewayAutoConfiguration, Spring Cloud Gateway core configuration class, initialization as follows:
NettyConfiguration
GlobalFilter
FilteringWebHandler
GatewayProperties
PrefixPathGatewayFilterFactory
RoutePredicateFactory
RouteDefinitionLocator
RouteLocator
RoutePredicateHandlerMapping
GatewayControllerEndpoint
The component relationship interaction is as follows:
2.5 opening and closing of gateway
From the annotation @ ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) on GatewayAutoConfiguration, we can see that:
Configure the opening and closing of the gateway through spring.cloud.gateway.enabled.
Matchifmissing = true = > gateway is on by default.
2.6 initialize NettyConfiguration
org.springframework.cloud.gateway.config.NettyConfiguration, Netty configuration class. The code is as follows:
@Configuration @ConditionalOnClass({HttpClient.class}) protected static class NettyConfiguration { protected NettyConfiguration() { } @Bean @ConditionalOnMissingBean public HttpClient httpClient(@Qualifier("nettyClientOptions") Consumer<? super Builder> options) { return HttpClient.create(options); } @Bean public Consumer<? super Builder> nettyClientOptions(HttpClientProperties properties) { return (opts) -> { Ssl ssl = properties.getSsl(); if (ssl.isUseInsecureTrustManager()) { opts.sslSupport((sslContextBuilder) -> { sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE); }); } Pool pool = properties.getPool(); PoolResources poolResources; if (pool.getType() == PoolType.FIXED) { poolResources = PoolResources.fixed(pool.getName(), pool.getMaxConnections(), pool.getAcquireTimeout()); } else { poolResources = PoolResources.elastic(pool.getName()); } opts.poolResources(poolResources); Proxy proxy = properties.getProxy(); if (StringUtils.hasText(proxy.getHost())) { opts.proxy((typeSpec) -> { reactor.ipc.netty.options.ClientProxyOptions.Builder builder = typeSpec.type(reactor.ipc.netty.options.ClientProxyOptions.Proxy.HTTP).host(proxy.getHost()); PropertyMapper map = PropertyMapper.get(); proxy.getClass(); map.from(proxy::getPort).whenNonNull().to(builder::port); proxy.getClass(); map.from(proxy::getUsername).whenHasText().to(builder::username); proxy.getClass(); map.from(proxy::getPassword).whenHasText().to((password) -> { builder.password((s) -> { return password; }); }); proxy.getClass(); map.from(proxy::getNonProxyHostsPattern).whenHasText().to(builder::nonProxyHosts); return builder; }); } }; }
2.7 initialize GlobalFilter
The code is as follows:
@Bean public RouteToRequestUrlFilter routeToRequestUrlFilter() { return new RouteToRequestUrlFilter(); } @Bean @ConditionalOnBean({DispatcherHandler.class}) public ForwardRoutingFilter forwardRoutingFilter(DispatcherHandler dispatcherHandler) { return new ForwardRoutingFilter(dispatcherHandler); } @Bean public WebSocketService webSocketService() { return new HandshakeWebSocketService(); } @Bean public WebsocketRoutingFilter websocketRoutingFilter(WebSocketClient webSocketClient, WebSocketService webSocketService, ObjectProvider<List<HttpHeadersFilter>> headersFilters) { return new WebsocketRoutingFilter(webSocketClient, webSocketService, headersFilters); }
2.8 initialize filteringwehandler
When all org.springframework.cloud.gateway.filter.GlobalFilter initialization is completed (including the above NettyRoutingFilter / NettyWriteResponseFilter), create a Bean object of type org.springframework.cloud.gateway.handler.filteringwehandler. The code is as follows:
@Bean public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) { return new FilteringWebHandler(globalFilters); }
2.9 initialize GatewayProperties
Create a Bean object of type org.springframework.cloud.gateway.config.GatewayProperties to load the RouteDefinition / FilterDefinition of the configuration file configuration. The code is as follows:
@Bean public GatewayProperties gatewayProperties() { return new GatewayProperties(); }
3.0 initialize PrefixPathGatewayFilterFactory
In link Create the implementers of the org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory interface under the org.springframework.cloud.gateway.filter.factory package.
3.1 initialize RoutePredicateFactory
In Links Create the implementers of the org.springframework.cloud.gateway.handler.predict.routepredictefactory interface under the org.springframework.cloud.gateway.handler.predict package.
3.2 initialize RouteDefinitionLocator
The code is as follows:
@Bean @ConditionalOnMissingBean public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) { return new PropertiesRouteDefinitionLocator(properties); } @Bean @ConditionalOnMissingBean({RouteDefinitionRepository.class}) public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() { return new InMemoryRouteDefinitionRepository(); } @Bean @Primary public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) { return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators)); }
3.3 initialize RouteLocator
The code is as follows:
@Bean public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> GatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties); } @Bean @Primary public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) { return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators))); }
In addition, there are two ways to implement a custom RouteLocator:
Use the routes ා locator() ා build() method to create a RouteLocator. The example code is as follows:
@Bean public RouteLocator customRouteLocator() { //@formatter:off return Routes.locator() .route("test") .predicate(host("**.abc.org").and(path("/image/png"))) .addResponseHeader("X-TestHeader", "foobar") .uri("http://httpbin.org:80") .route("test2") .predicate(path("/image/webp")) .add(addResponseHeader("X-AnotherHeader", "baz")) .uri("http://httpbin.org:80") .build(); ////@formatter:on }
Use routelocatordsl ා gateway() method to create RouteLocator, which is implemented by Kotlin. The example code is as follows:
@Configuration class AdditionalRoutes { @Bean fun additionalRouteLocator(): RouteLocator = gateway { route(id = "test-kotlin") { uri("http://httpbin.org:80") predicate(host("kotlin.abc.org") and path("/image/png")) add(addResponseHeader("X-TestHeader", "foobar")) } } }
3.4 initialize RoutePredicateHandlerMapping
Create a Bean object of type org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping, which is used to find and match routes, and process it. The code is as follows:
@Bean public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator) { return new RoutePredicateHandlerMapping(webHandler, routeLocator); }
3.5 initialize GatewayWebfluxEndpoint
Create a Bean object of type org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint to provide the HTTP API for managing gateways. The code is as follows:
@Configuration @ConditionalOnClass({Health.class}) protected static class GatewayActuatorConfiguration { protected 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. Spring Cloud gateway request entry analysis
No matter Zuul, spring cloud gateway or self-developed gateway based on Netty, the Request or the returned Response will be packaged, transformed and extracted as the context information of the gateway operation, while in spring cloud gateway, the context of the gateway is ServerWebExchange.
4.1 import HttpServerRequest and HttpServerResponse conversion
Request entry of Spring Cloud Gateway, org.springframework.http.server.reactive.reactorhttphandleradapter ා apply method
The code comes from spring-web-5.0.4.RELEASE.jar. This method is the request entry method of Spring Cloud Gateway. The function of this method is to convert the received HttpServerRequest or the final HttpServerResponse to reactor server httprequest and reactor server httpresponse.
4.2 construct the context ServerWebExchange of Spring Cloud gateway
In line 91 of org.springframework.web.server.adapter.HttpWebHandlerAdapter, the code is as follows:
createExchange() builds the gateway context ServerWebExchange from ServerHttpRequest ServerHttpResponse.
PS:org.springframework.web.server.handler.WebHandlerDecorator.getDelegate() obtains a series of webhandlers to be processed through delegation
4.3 enter the Filter chain
Org.springframework.cloud.gateway.handler.filteringwebhandler ා handle method, i.e. line 46, the code is as follows: