1.简述
由于加入了公司的微服务组,最近在做一些网关方面的工作,由于公司使用了Gateway 作为网关的技术选型,所以只有自己充分了解才能在之后的开发上得心应手。
由于 Gateway 使用了 Reactor 来构建非堵塞
的编程模型。自己对这一块不是很熟悉所以对自己也是一个挑战,尝试分析若有不妥之处,望见谅。
下面是基于 spring-cloud-gateway-2.1.x
进行分析
2.Gateway 工作机制
Spring cloud gateway 的工作机制如下图所示:
流程大致如下:
- 客户端发送请求 (Gateway Client)
- Gateway 接收到请求后,跟路由的Predicate 进行匹配。(Gateway Handler Mapping)
- 如果匹配成功,发送到下游的 Filiter Chain (Gateway Web Handler)
- 请求经过 Filter 过滤器链,执行 “pre” 处理逻辑,如修改请求头信息等。(Filter Chain)
- 请求被转发至下游服务并返回响应(Proxied Service)
- 响应会在经过 Filter Chain,执行 “post” 处理逻辑。(Filter Chain)
- 向客户端响应应答。
注意:如果在路由里没有定义端口,那么就会根据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的,这里判断导入的包是否是webfluxGatewayRedisAutoConfiguration
:用于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进行加载。