Gateway(一)初始化

2020年4月18日 | 作者 Siran | 3300字 | 阅读大约需要7分钟
归档于 gateway | 标签 #gateway

1.简述

由于加入了公司的微服务组,最近在做一些网关方面的工作,由于公司使用了Gateway 作为网关的技术选型,所以只有自己充分了解才能在之后的开发上得心应手。

由于 Gateway 使用了 Reactor 来构建非堵塞的编程模型。自己对这一块不是很熟悉所以对自己也是一个挑战,尝试分析若有不妥之处,望见谅。

下面是基于 spring-cloud-gateway-2.1.x 进行分析


2.Gateway 工作机制

Spring cloud gateway 的工作机制如下图所示:

流程大致如下:

  1. 客户端发送请求 (Gateway Client)
  2. Gateway 接收到请求后,跟路由的Predicate 进行匹配。(Gateway Handler Mapping)
  3. 如果匹配成功,发送到下游的 Filiter Chain (Gateway Web Handler)
  4. 请求经过 Filter 过滤器链,执行 “pre” 处理逻辑,如修改请求头信息等。(Filter Chain)
  5. 请求被转发至下游服务并返回响应(Proxied Service)
  6. 响应会在经过 Filter Chain,执行 “post” 处理逻辑。(Filter Chain)
  7. 向客户端响应应答。

注意:如果在路由里没有定义端口,那么就会根据HTTP / HTTPS 默认获得 80 / 443 端口


3.核心组件

通过上面的工作机制可以知道,核心组件有以下几个:

  • Route

    public class
    Route implements Ordered {
      
      //id,标识符,区别于其他 Route。
      private final String id;
      
      //destination uri,路由指向的目的地 uri,即客户端请求最终被转发的目的地。
      private final URI uri;
      
      //order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
      private final int order;
      
      //predicate,谓语,表示匹配该 Route 的前置条件,即满足相应的条件才会被路由到目的地 uri。
      private final AsyncPredicate<ServerWebExchange> predicate;
      
      //gateway filters,过滤器用于处理切面逻辑,如路由转发前修改请求头等。
      private final List<GatewayFilter> gatewayFilters;
    }	
    

    可以看到,Route 和 Predicate 、Filter 是一种包含关系,即一个Route 下面可以有多个Predicate 和Filter ,Route会通过Order 进行排序,每个Route都是唯一的 通过Id 标识。

  • Predicate

    在 Gateway 工作机制中,讲过当客户端发送请求过来的时候,首先会通过Predicate 进行匹配,匹配到才会进行下面的操作。

    public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
      
      //① and ,与操作,即两个 Predicate 组成一个,需要同时满足。
      default AsyncPredicate<T> and(AsyncPredicate<? super T> other) {
          return new AndAsyncPredicate<>(this, other);
      }
      
      //② negate,取反操作,即对 Predicate 匹配结果取反。
      default AsyncPredicate<T> negate() {
          return new NegateAsyncPredicate<>(this);
      }
      
      //③ or,或操作,即两个 Predicate 组成一个,只需满足其一。
      default AsyncPredicate<T> or(AsyncPredicate<? super T> other) {
          return new OrAsyncPredicate<>(this, other);
      }
    
    • ① and 默认方法(Java8 新特性):与操作,即两个 Predicate 组成一个,需要同时满足。

    • ② negate :取反操作,即对 Predicate 匹配结果取反。

    • ③ or :或操作,即两个 Predicate 组成一个,只需满足其一。

  • Filter

    通过Predicate 匹配成功后,就会经过过滤链,进行 “pre” 处理。

    public interface GatewayFilter extends ShortcutConfigurable {
      
      /**
       * Name key.
       */
      String NAME_KEY = "name";
      
      /**
       * Value key.
       */
      String VALUE_KEY = "value";
      
      Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
    }
    

    到这里,Gateway 的核心组件就介绍完了。以官网的例子,来方便理解

    spring:
      cloud:
        gateway:
          routes:  //① 配置route,可以有多个
          - id: after_route //② route id,用于表示route
            uri: https://example.org   //③ 此路由转发的目的地url
            predicates:  //④ 谓语,客户端传过来的请求满足此才能使用该路由
            - Cookie=mycookie,mycookievalue
            filters: //⑤ filter
            - AddRequestHeader=X-Request-Foo, Bar
    
    • ① 配置route,可以有多个
    • ② route id,用于表示route
    • ③ 此路由转发的目的地url
    • ④ 谓语,客户端传过来的请求满足此才能使用该路由
    • ⑤ filter

    跟上面的核心组件以及gateway流程结合起来看,应该能有一个很好的认识。接下来我们从Gateway启动初始化一步一步的进行分析


4.Gateway 初始化

由于Gateway是基于Springboot 构建的,所以会用到Springboot的自动装配。

直接拿官方的例子来看一下Gateway 初始化加载了哪些组件。

//org.springframework.cloud.gateway.sample.GatewaySampleApplication
@SpringBootConfiguration 
@EnableAutoConfiguration //自动装配
@Import(AdditionalRoutes.class)
public class GatewaySampleApplication {
	public static void main(String[] args) {
		SpringApplication.run(GatewaySampleApplication.class, args);
	}
}

一个标准的Springboot 启动方式,因为Gateway 是通过Springboot构建的。

直接看来一下Gateway 自动装配加载了些什么。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\ //重点关注
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,\
org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration,\
org.springframework.cloud.gateway.config.SimpleUrlHandlerMappingGlobalCorsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration

  • GatewayClassPathWarningAutoConfiguration:由于Gateway是基于webflux的,这里判断导入的包是否是webflux
  • GatewayRedisAutoConfiguration:用于Redis限流
  • GatewayLoadBalancerClientAutoConfiguration:用于负载均衡
  • GatewayAutoConfiguration:核心配置类

5.GatewayAutoConfiguration

@Configuration
//通过 spring.cloud.gateway.enabled 配置网关的开启与关闭。 默认是 打开的
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class,
		WebFluxAutoConfiguration.class })
@AutoConfigureAfter({ GatewayLoadBalancerClientAutoConfiguration.class,
		GatewayClassPathWarningAutoConfiguration.class })
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {

  //用于构建Route的 Builder
	@Bean
	public RouteLocatorBuilder routeLocatorBuilder(
			ConfigurableApplicationContext context) {
		return new RouteLocatorBuilder(context);
	}

  //RouteDefinitionLocator 的实现类,用于从配置文件(Properties,yml) 获取Route配置
	@Bean
	@ConditionalOnMissingBean
	public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(
			GatewayProperties properties) {
		//RouteDefinitionLocator 的实现类,RouteDefinition 信息来自 GatewayProperties。
		return new PropertiesRouteDefinitionLocator(properties);
	}

  //RouteDefinitionLocator 的实现类,基于内存的存储器( 例如,内存 / Redis / MySQL 等 )读取、保存、删除路由配置。
	@Bean
	@ConditionalOnMissingBean(RouteDefinitionRepository.class)
	public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
		return new InMemoryRouteDefinitionRepository();
	}

	/*
	声明 beanrouteDefinitionLocator,使用 CompositeRouteDefinitionLocator 实现,它组合了多个 RouteDefinitionLocator 实例。
	这给用户(开发者)提供了可扩展的余地,用户可以根据需要扩展自己的 RouteDefinitionLocator,比如 RouteDefinition 可源自数据库。
	 */
	@Bean
	@Primary
	public RouteDefinitionLocator routeDefinitionLocator(
			List<RouteDefinitionLocator> routeDefinitionLocators) {
		return new CompositeRouteDefinitionLocator(
				Flux.fromIterable(routeDefinitionLocators));
	}

  //基于 RouteDefinitionLocator 的 RouteLocator 实现类。RouteDefinitionRouteLocator 从 RouteDefinitionLocator 获取 RouteDefinition ,转换成 Route
	@Bean
	public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
			List<GatewayFilterFactory> gatewayFilters,
			List<RoutePredicateFactory> predicates,
			RouteDefinitionLocator routeDefinitionLocator,
			@Qualifier("webFluxConversionService") ConversionService conversionService) {
		return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
				gatewayFilters, properties, conversionService);
	}

  //监听route的更新
	@Bean
	public RouteRefreshListener routeRefreshListener(
			ApplicationEventPublisher publisher) {
		return new RouteRefreshListener(publisher);
	}

  //FilteringWebHandler 通过创建请求对应的 Route 对应的 GatewayFilterChain 进行处理。
	@Bean
	public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
		return new FilteringWebHandler(globalFilters);
	}

  
  //用于查找匹配到 Route ,并进行处理
	@Bean
	public RoutePredicateHandlerMapping routePredicateHandlerMapping(
			FilteringWebHandler webHandler, RouteLocator routeLocator,
			GlobalCorsProperties globalCorsProperties, Environment environment) {
		return new RoutePredicateHandlerMapping(webHandler, routeLocator,
				globalCorsProperties, environment);
	}

	//是 Spring cloud gateway 模块提供的外部化配置类。
	@Bean
	public GatewayProperties gatewayProperties() {
		return new GatewayProperties();
	}
  
  //...省略一些内置的Filter 和 Predicate

	//初始化 NettyConfiguration  Netty配置类
	@Configuration
	@ConditionalOnClass(HttpClient.class)
	protected static class NettyConfiguration {

		@Bean
		@ConditionalOnProperty(name = "spring.cloud.gateway.httpserver.wiretap")
		public NettyWebServerFactoryCustomizer nettyServerWiretapCustomizer(
				Environment environment, ServerProperties serverProperties) {
			return new NettyWebServerFactoryCustomizer(environment, serverProperties) {
				@Override
				public void customize(NettyReactiveWebServerFactory factory) {
					factory.addServerCustomizers(httpServer -> httpServer.wiretap(true));
					super.customize(factory);
				}
			};
		}

		//创建一个类型为 reactor.ipc.netty.http.client.HttpClient 的 Bean 对象。该 HttpClient 使用 Netty 实现的 Client 。
		@Bean
		@ConditionalOnMissingBean
		public HttpClient gatewayHttpClient(HttpClientProperties properties) {

			// configure pool resources
			HttpClientProperties.Pool pool = properties.getPool();

			ConnectionProvider connectionProvider;
			if (pool.getType() == DISABLED) {
				connectionProvider = ConnectionProvider.newConnection();
			}
			else if (pool.getType() == FIXED) {
				connectionProvider = ConnectionProvider.fixed(pool.getName(),
						pool.getMaxConnections(), pool.getAcquireTimeout());
			}
			else {
				connectionProvider = ConnectionProvider.elastic(pool.getName());
			}

			HttpClient httpClient = HttpClient.create(connectionProvider)
					.tcpConfiguration(tcpClient -> {

						if (properties.getConnectTimeout() != null) {
							tcpClient = tcpClient.option(
									ChannelOption.CONNECT_TIMEOUT_MILLIS,
									properties.getConnectTimeout());
						}

						// configure proxy if proxy host is set.
						HttpClientProperties.Proxy proxy = properties.getProxy();

						if (StringUtils.hasText(proxy.getHost())) {

							tcpClient = tcpClient.proxy(proxySpec -> {
								ProxyProvider.Builder builder = proxySpec
										.type(ProxyProvider.Proxy.HTTP)
										.host(proxy.getHost());

								PropertyMapper map = PropertyMapper.get();

								map.from(proxy::getPort).whenNonNull().to(builder::port);
								map.from(proxy::getUsername).whenHasText()
										.to(builder::username);
								map.from(proxy::getPassword).whenHasText()
										.to(password -> builder.password(s -> password));
								map.from(proxy::getNonProxyHostsPattern).whenHasText()
										.to(builder::nonProxyHosts);
							});
						}
						return tcpClient;
					});

			HttpClientProperties.Ssl ssl = properties.getSsl();
			if (ssl.getTrustedX509CertificatesForTrustManager().length > 0
					|| ssl.isUseInsecureTrustManager()) {
				httpClient = httpClient.secure(sslContextSpec -> {
					// configure ssl
					SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();

					X509Certificate[] trustedX509Certificates = ssl
							.getTrustedX509CertificatesForTrustManager();
					if (trustedX509Certificates.length > 0) {
						sslContextBuilder.trustManager(trustedX509Certificates);
					}
					else if (ssl.isUseInsecureTrustManager()) {
						sslContextBuilder
								.trustManager(InsecureTrustManagerFactory.INSTANCE);
					}

					sslContextSpec.sslContext(sslContextBuilder)
							.defaultConfiguration(ssl.getDefaultConfigurationType())
							.handshakeTimeout(ssl.getHandshakeTimeout())
							.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
							.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
				});
			}

			if (properties.isWiretap()) {
				httpClient = httpClient.wiretap(true);
			}

			return httpClient;
		}
    
    //...省略一些内置的Filter 和 Predicate
}
  • NettyConfiguration:Netty 配置类

  • GlobalFilter:Filter的父接口,称为全局过滤器,初始化的一些Filter都是它的实现

    @Bean
    public RouteToRequestUrlFilter routeToRequestUrlFilter() {
          return new RouteToRequestUrlFilter();
      }
    public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {
        
    }
    

    还有很多,由于篇幅问题不一一举例,可自行查看。

  • FilteringWebHandler:在Gateway工作机制中提到过,一个请求过来,Gateway Handler Mapping 会跟Predicate 进行匹配,匹配成功后就是调用 FilteringWebHandler 来调用Filter Chain队请求进行处理。

  • GatewayProperties:用于加载配置文件配置的 RouteDefinition / FilterDefinition /PredicateDefinition

  • RoutePredicateFactory:Predicate 的父类

    @Bean
    public BeforeRoutePredicateFactory beforeRoutePredicateFactory() {
          return new BeforeRoutePredicateFactory();
    }
    public class BeforeRoutePredicateFactory
          extends AbstractRoutePredicateFactory<BeforeRoutePredicateFactory.Config> {
    }
    public abstract class AbstractRoutePredicateFactory<C> extends AbstractConfigurable<C>
          implements RoutePredicateFactory<C> {
    }
    

    还有很多,由于篇幅问题不一一举例,可自行查看。

  • RouteDefinitionLocator:加载外置配置的父类。

    //RouteDefinitionLocator 的实现类,RouteDefinition 信息来自 GatewayProperties。
    @Bean
    @ConditionalOnMissingBean
    public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(
              GatewayProperties properties) {
          return new PropertiesRouteDefinitionLocator(properties);
      }
      
    //RouteDefinitionLocator 的实现类,基于内存的存储器( 例如,内存 / Redis / MySQL 等 )读取、保存、删除路由配置
    @Bean
    @ConditionalOnMissingBean(RouteDefinitionRepository.class)
    public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
          return new InMemoryRouteDefinitionRepository();
      }
    

    还有很多,由于篇幅问题不一一举例,可自行查看。

  • RouteLocator:加载编码配置

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
       builder.routes()
                  .route(r -> r.cookie("mycookie","mycookievalue")
                          .filters(f -> f.addRequestHeader("X-Request-Foo","Bar"))
                          .uri("https://example.org")
                  )
                  .build();
    }
    
  • RoutePredicateHandlerMapping:在Gateway工作机制中提到过,一个请求过来,Gateway HandlerMapping 会跟Predicate 进行匹配

  • GatewayWebfluxEndpoint:提供管理网关的 HTTP API


到这里Gateway初始化的所有组件全部分析完毕,接下来会对所有的组件一一进行分析。在看一下这张图。会发现就是把这里的HanlderMapping、WebHandler 、Filter进行加载。