目录
GatewayClassPathWarningAutoConfiguration类
GatewayHystrixCircuitBreakerAutoConfiguration类
GatewayLoadBalancerClientAutoConfiguration类
GatewayNoLoadBalancerClientAutoConfiguration类
GatewayMetricsAutoConfiguration类
GatewayRedisAutoConfiguration类
GatewayDiscoveryClientAutoConfiguration类
SimpleUrlHandlerMappingGlobalCorsAutoConfiguration类
GatewayReactiveLoadBalancerClientAutoConfiguration类
2、InMemoryRouteDefinitionRepository提供save/delete操作RouteDefinition(路由对象)
3、CompositeRouteDefinitionLocator提供将RouteDefinition加载到RouteDefinitionLocator
5、CachingRouteLocator提供由applicatEventPublisther自动刷新对Route缓存
1、调用api接口,由于自定义Filter的order一般很小(如tokenFilter -100之类),请求从经过该类Filter
2、进入DispatcherHandler.handle()
3、进入RoutePredicateHandlerMapping
4、进入FilteringWebHandler根据GatewayFilterChain中Filters列表按order值从小到大,依次执行filters方法调用
4.1、LoadBalancerClientFilter负载均衡
概况
版本:2.2.4.RELEASE
服务注册由Nacos提供
pom解析
从spring-cloud-gateway pom中可看出其核心依赖包括:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
<version>2.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
//core
//包含:spring-boot-starter/spring-boot-starter-security/spring-boot-autoconfigure-processor/spring-boot-starter-data-redis
//spring-cloud-starter-netflix-ribbon/spring-cloud-starter-netflix-hystrix...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-core</artifactId>
<version>2.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
//webflux
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.3.2.RELEASE</version>
<scope>compile</scope>
</dependency>
//负载均衡
//包含:spring-cloud-starter/spring-cloud-loadbalancer/spring-boot-starter-cache...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>2.2.4.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
spring.factories解析
注意:在3.0.0.RELEASE中,已经GatewayReactiveLoadBalancerClientAutoConfiguration替代GatewayResilience4JCircuitBreakerAutoConfiguration实现ReactiveLoadBalancerClientFilter。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayHystrixCircuitBreakerAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration,\
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
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.gateway.config.GatewayEnvironmentPostProcessor
GatewayClassPathWarningAutoConfiguration类
classPath检测
注意依赖org.springframework.web.servlet.DispatcherServlet
@Configuration(
proxyBeanMethods = false
)
@AutoConfigureBefore({GatewayAutoConfiguration.class})
public class GatewayClassPathWarningAutoConfiguration {
...
//检测spring-boot-starter-webflux dependency有否存在
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingClass({"org.springframework.web.reactive.DispatcherHandler"})
protected static class WebfluxMissingFromClasspathConfiguration {
public WebfluxMissingFromClasspathConfiguration() {
GatewayClassPathWarningAutoConfiguration.log.warn("\n\n**********************************************************\n\nSpring Webflux is missing from the classpath, which is required for Spring Cloud Gateway at this time. Please add spring-boot-starter-webflux dependency.\n\n**********************************************************\n\n");
}
}
//检测spring-boot-starter-web dependency是否去除
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass(
name = {"org.springframework.web.servlet.DispatcherServlet"}
)
protected static class SpringMvcFoundOnClasspathConfiguration {
public SpringMvcFoundOnClasspathConfiguration() {
GatewayClassPathWarningAutoConfiguration.log.warn("\n\n**********************************************************\n\nSpring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.\n\n**********************************************************\n\n");
}
}
}
GatewayAutoConfiguration类
核心类
内置各类Filter 与 Factory 实例化
详情见源码注解:
@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 {
//属性对象实例化
@Bean
public RouteLocatorBuilder routeLocatorBuilder(ConfigurableApplicationContext context) {
return new RouteLocatorBuilder(context);
}
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
return new PropertiesRouteDefinitionLocator(properties);
}
//RouteDefinition的存储,RouteDefinition会在后面转换成Route: Map<String, RouteDefinition>
@Bean
@ConditionalOnMissingBean({RouteDefinitionRepository.class})
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();
}
//注入所有RouteDefinitionLocator,配置的路由通过子类PropertiesRouteDefinitionLocator来完成的,
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
}
@Bean
public ConfigurationService gatewayConfigurationService(BeanFactory beanFactory, @Qualifier("webFluxConversionService") ObjectProvider<ConversionService> conversionService, ObjectProvider<Validator> validator) {
return new ConfigurationService(beanFactory, conversionService, validator);
}
//RouteDefinitionLocator 作为参数注入到routeDefinitionLocator
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties, configurationService);
}
//提供缓存RouteLocator
@Bean
@Primary
@ConditionalOnMissingBean(
name = {"cachedCompositeRouteLocator"}
)
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
//提供刷新RouteLocator
@Bean
public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {
return new RouteRefreshListener(publisher);
}
@Bean
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
return new FilteringWebHandler(globalFilters);
}
@Bean
public GlobalCorsProperties globalCorsProperties() {
return new GlobalCorsProperties();
}
@Bean
public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);
}
@Bean
public GatewayProperties gatewayProperties() {
return new GatewayProperties();
}
@Bean
public SecureHeadersProperties secureHeadersProperties() {
return new SecureHeadersProperties();
}
//开始filter实例化
//forwardedHeader路由
@Bean
@ConditionalOnProperty(
name = {"spring.cloud.gateway.forwarded.enabled"},
matchIfMissing = true
)
public ForwardedHeadersFilter forwardedHeadersFilter() {
return new ForwardedHeadersFilter();
}
@Bean
public RemoveHopByHopHeadersFilter removeHopByHopHeadersFilter() {
return new RemoveHopByHopHeadersFilter();
}
@Bean
@ConditionalOnProperty(
name = {"spring.cloud.gateway.x-forwarded.enabled"},
matchIfMissing = true
)
public XForwardedHeadersFilter xForwardedHeadersFilter() {
return new XForwardedHeadersFilter();
}
@Bean
public AdaptCachedBodyGlobalFilter adaptCachedBodyGlobalFilter() {
return new AdaptCachedBodyGlobalFilter();
}
@Bean
public RemoveCachedBodyFilter removeCachedBodyFilter() {
return new RemoveCachedBodyFilter();
}
//基于请求URI创建一个新URI,但使用Route对象的URI属性进行更新。适用于lb:ws://serviceid
//lb方案从URI中剥离,并放入中以ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR供稍后在过滤器链中使用
@Bean
public RouteToRequestUrlFilter routeToRequestUrlFilter() {
return new RouteToRequestUrlFilter();
}
//forward 正向路由
@Bean
public ForwardRoutingFilter forwardRoutingFilter(ObjectProvider<DispatcherHandler> dispatcherHandler) {
return new ForwardRoutingFilter(dispatcherHandler);
}
//ForwardPathFilter路由
@Bean
public ForwardPathFilter forwardPathFilter() {
return new ForwardPathFilter();
}
@Bean
public WebSocketService webSocketService(RequestUpgradeStrategy requestUpgradeStrategy) {
return new HandshakeWebSocketService(requestUpgradeStrategy);
}
//websocket路由过滤器
//routes:
// - id: websocket_route
// uri: http://localhost:8080 也可使用lb来负载均衡
// predicates:
// - Path=/websocket/info/**
@Bean
public WebsocketRoutingFilter websocketRoutingFilter(WebSocketClient webSocketClient, WebSocketService webSocketService, ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
return new WebsocketRoutingFilter(webSocketClient, webSocketService, headersFilters);
}
@Bean
public WeightCalculatorWebFilter weightCalculatorWebFilter(ConfigurationService configurationService, ObjectProvider<RouteLocator> routeLocator) {
return new WeightCalculatorWebFilter(routeLocator, configurationService);
}
//各类Factory实例化
//Route Predicate Factory
//predicates:
// - After=2020-12-18T17:42:47.789-07:00[XXXX]
@Bean
public AfterRoutePredicateFactory afterRoutePredicateFactory() {
return new AfterRoutePredicateFactory();
}
//predicates:
// - Before=2020-12-18T17:42:47.789-07:00[XXXX]
@Bean
public BeforeRoutePredicateFactory beforeRoutePredicateFactory() {
return new BeforeRoutePredicateFactory();
}
//predicates:
// - Between=2020-12-18T17:42:47.789-07:00[XXXX],2020-12-19T17:42:47.789-07:00[XXXX]
@Bean
public BetweenRoutePredicateFactory betweenRoutePredicateFactory() {
return new BetweenRoutePredicateFactory();
}
//predicates:
// - Cookie=chocolate, xxx...
@Bean
public CookieRoutePredicateFactory cookieRoutePredicateFactory() {
return new CookieRoutePredicateFactory();
}
//predicates:
// - Header=X-Request-Id, \d+
@Bean
public HeaderRoutePredicateFactory headerRoutePredicateFactory() {
return new HeaderRoutePredicateFactory();
}
//predicates:
// - Host=**.xxx.com,**.xxx.org
@Bean
public HostRoutePredicateFactory hostRoutePredicateFactory() {
return new HostRoutePredicateFactory();
}
//predicates:
// - Method=GET
@Bean
public MethodRoutePredicateFactory methodRoutePredicateFactory() {
return new MethodRoutePredicateFactory();
}
//predicates:
// - Path=/foo/{segment},/bar/{segment}
@Bean
public PathRoutePredicateFactory pathRoutePredicateFactory() {
return new PathRoutePredicateFactory();
}
//predicates:
// - Query=querxxx
@Bean
public QueryRoutePredicateFactory queryRoutePredicateFactory() {
return new QueryRoutePredicateFactory();
}
@Bean
public ReadBodyPredicateFactory readBodyPredicateFactory(ServerCodecConfigurer codecConfigurer) {
return new ReadBodyPredicateFactory(codecConfigurer.getReaders());
}
//predicates:
// - RemoteAddr=192.168.10.1/24
@Bean
public RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory() {
return new RemoteAddrRoutePredicateFactory();
}
//predicates:
// - Weight=group1, 2
@Bean
@DependsOn({"weightCalculatorWebFilter"})
public WeightRoutePredicateFactory weightRoutePredicateFactory() {
return new WeightRoutePredicateFactory();
}
@Bean
public CloudFoundryRouteServiceRoutePredicateFactory cloudFoundryRouteServiceRoutePredicateFactory() {
return new CloudFoundryRouteServiceRoutePredicateFactory();
}
//GatewayFilter工厂
//filters:
// - AddRequestHeader=X-Request-Foo, Bar
@Bean
public AddRequestHeaderGatewayFilterFactory addRequestHeaderGatewayFilterFactory() {
return new AddRequestHeaderGatewayFilterFactory();
}
//filters:
// - MapRequestHeader=Bar, X-Request-Foo
@Bean
public MapRequestHeaderGatewayFilterFactory mapRequestHeaderGatewayFilterFactory() {
return new MapRequestHeaderGatewayFilterFactory();
}
//filters:
// - AddRequestParameter=foo, bar
@Bean
public AddRequestParameterGatewayFilterFactory addRequestParameterGatewayFilterFactory() {
return new AddRequestParameterGatewayFilterFactory();
}
//filters:
// - AddResponseHeader=X-Response-Foo, Bar
@Bean
public AddResponseHeaderGatewayFilterFactory addResponseHeaderGatewayFilterFactory() {
return new AddResponseHeaderGatewayFilterFactory();
}
@Bean
public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory(ServerCodecConfigurer codecConfigurer) {
return new ModifyRequestBodyGatewayFilterFactory(codecConfigurer.getReaders());
}
//filters:
// - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
@Bean
public DedupeResponseHeaderGatewayFilterFactory dedupeResponseHeaderGatewayFilterFactory() {
return new DedupeResponseHeaderGatewayFilterFactory();
}
//filters:
// - PrefixPath=/mypath
@Bean
public PrefixPathGatewayFilterFactory prefixPathGatewayFilterFactory() {
return new PrefixPathGatewayFilterFactory();
}
//filters:
// - PreserveHostHeader
@Bean
public PreserveHostHeaderGatewayFilterFactory preserveHostHeaderGatewayFilterFactory() {
return new PreserveHostHeaderGatewayFilterFactory();
}
...
//RateLimiter实现来确定是否允许继续当前请求
@Bean(
name = {"principalNameKeyResolver"}
)
@ConditionalOnBean({RateLimiter.class})
@ConditionalOnMissingBean({KeyResolver.class})
public PrincipalNameKeyResolver principalNameKeyResolver() {
return new PrincipalNameKeyResolver();
}
@Bean
@ConditionalOnBean({RateLimiter.class, KeyResolver.class})
public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory(RateLimiter rateLimiter, KeyResolver resolver) {
return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver);
}
//rewrite & set filters
@Bean
public RewritePathGatewayFilterFactory rewritePathGatewayFilterFactory() {
return new RewritePathGatewayFilterFactory();
}
@Bean
public RetryGatewayFilterFactory retryGatewayFilterFactory() {
return new RetryGatewayFilterFactory();
}
@Bean
public SetPathGatewayFilterFactory setPathGatewayFilterFactory() {
return new SetPathGatewayFilterFactory();
}
....
@Bean
public SaveSessionGatewayFilterFactory saveSessionGatewayFilterFactory() {
return new SaveSessionGatewayFilterFactory();
}
@Bean
public StripPrefixGatewayFilterFactory stripPrefixGatewayFilterFactory() {
return new StripPrefixGatewayFilterFactory();
}
@Bean
public RequestHeaderToRequestUriGatewayFilterFactory requestHeaderToRequestUriGatewayFilterFactory() {
return new RequestHeaderToRequestUriGatewayFilterFactory();
}
@Bean
public RequestSizeGatewayFilterFactory requestSizeGatewayFilterFactory() {
return new RequestSizeGatewayFilterFactory();
}
@Bean
public RequestHeaderSizeGatewayFilterFactory requestHeaderSizeGatewayFilterFactory() {
return new RequestHeaderSizeGatewayFilterFactory();
}
...
//hystrix
//filters:
// - Hystrix=myCommandName
//或
// filters:
// - name: Hystrix //定义hystrix
// args:
// name: fetchIngredients
// fallbackUri: forward:/fallback
//- id: ingredients-fallback //定义fallback访问
// uri: http://localhost:9994
// predicates:
// - Path=/fallback
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({HystrixObservableCommand.class, RxReactiveStreams.class})
protected static class HystrixConfiguration {
protected HystrixConfiguration() {
}
@Bean
public HystrixGatewayFilterFactory hystrixGatewayFilterFactory(ObjectProvider<DispatcherHandler> dispatcherHandler) {
return new HystrixGatewayFilterFactory(dispatcherHandler);
}
@Bean
@ConditionalOnMissingBean({FallbackHeadersGatewayFilterFactory.class})
public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() {
return new FallbackHeadersGatewayFilterFactory();
}
}
//netty filter
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({HttpClient.class})
protected static class NettyConfiguration {
protected final Log logger = LogFactory.getLog(this.getClass());
protected NettyConfiguration() {
}
@Bean
@ConditionalOnProperty(
name = {"spring.cloud.gateway.httpserver.wiretap"}
)
public NettyWebServerFactoryCustomizer nettyServerWiretapCustomizer(Environment environment, ServerProperties serverProperties) {
return new NettyWebServerFactoryCustomizer(environment, serverProperties) {
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers(new NettyServerCustomizer[]{(httpServer) -> {
return httpServer.wiretap(true);
}});
super.customize(factory);
}
};
}
@Bean
@ConditionalOnMissingBean
public HttpClient gatewayHttpClient(HttpClientProperties properties, List<HttpClientCustomizer> customizers) {
Pool pool = properties.getPool();
ConnectionProvider connectionProvider;
if (pool.getType() == PoolType.DISABLED) {
connectionProvider = ConnectionProvider.newConnection();
} else if (pool.getType() == PoolType.FIXED) {
connectionProvider = ConnectionProvider.fixed(pool.getName(), pool.getMaxConnections(), pool.getAcquireTimeout(), pool.getMaxIdleTime(), pool.getMaxLifeTime());
} else {
connectionProvider = ConnectionProvider.elastic(pool.getName(), pool.getMaxIdleTime(), pool.getMaxLifeTime());
}
HttpClient httpClient = HttpClient.create(connectionProvider).httpResponseDecoder((spec) -> {
if (properties.getMaxHeaderSize() != null) {
spec.maxHeaderSize((int)properties.getMaxHeaderSize().toBytes());
}
if (properties.getMaxInitialLineLength() != null) {
spec.maxInitialLineLength((int)properties.getMaxInitialLineLength().toBytes());
}
return spec;
}).tcpConfiguration((tcpClient) -> {
if (properties.getConnectTimeout() != null) {
tcpClient = tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, properties.getConnectTimeout());
}
Proxy proxy = properties.getProxy();
if (StringUtils.hasText(proxy.getHost())) {
tcpClient = tcpClient.proxy((proxySpec) -> {
Builder builder = proxySpec.type(reactor.netty.tcp.ProxyProvider.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 tcpClient;
});
Ssl ssl = properties.getSsl();
if (ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0 || ssl.getTrustedX509CertificatesForTrustManager().length > 0 || ssl.isUseInsecureTrustManager()) {
httpClient = httpClient.secure((sslContextSpec) -> {
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
X509Certificate[] trustedX509Certificates = ssl.getTrustedX509CertificatesForTrustManager();
if (trustedX509Certificates.length > 0) {
sslContextBuilder = sslContextBuilder.trustManager(trustedX509Certificates);
} else if (ssl.isUseInsecureTrustManager()) {
sslContextBuilder = sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
}
try {
sslContextBuilder = sslContextBuilder.keyManager(ssl.getKeyManagerFactory());
} catch (Exception var6) {
this.logger.error(var6);
}
sslContextSpec.sslContext(sslContextBuilder).defaultConfiguration(ssl.getDefaultConfigurationType()).handshakeTimeout(ssl.getHandshakeTimeout()).closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout()).closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
});
}
if (properties.isWiretap()) {
httpClient = httpClient.wiretap(true);
}
if (!CollectionUtils.isEmpty(customizers)) {
customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
HttpClientCustomizer customizer;
for(Iterator var7 = customizers.iterator(); var7.hasNext(); httpClient = customizer.customize(httpClient)) {
customizer = (HttpClientCustomizer)var7.next();
}
}
return httpClient;
}
@Bean
public HttpClientProperties httpClientProperties() {
return new HttpClientProperties();
}
@Bean
public NettyRoutingFilter routingFilter(HttpClient httpClient, ObjectProvider<List<HttpHeadersFilter>> headersFilters, HttpClientProperties properties) {
return new NettyRoutingFilter(httpClient, headersFilters, properties);
}
@Bean
public NettyWriteResponseFilter nettyWriteResponseFilter(GatewayProperties properties) {
return new NettyWriteResponseFilter(properties.getStreamingMediaTypes());
}
@Bean
public ReactorNettyWebSocketClient reactorNettyWebSocketClient(HttpClientProperties properties, HttpClient httpClient) {
ReactorNettyWebSocketClient webSocketClient = new ReactorNettyWebSocketClient(httpClient);
if (properties.getWebsocket().getMaxFramePayloadLength() != null) {
webSocketClient.setMaxFramePayloadLength(properties.getWebsocket().getMaxFramePayloadLength());
}
webSocketClient.setHandlePing(properties.getWebsocket().isProxyPing());
return webSocketClient;
}
@Bean
public ReactorNettyRequestUpgradeStrategy reactorNettyRequestUpgradeStrategy(HttpClientProperties httpClientProperties) {
ReactorNettyRequestUpgradeStrategy requestUpgradeStrategy = new ReactorNettyRequestUpgradeStrategy();
Websocket websocket = httpClientProperties.getWebsocket();
PropertyMapper map = PropertyMapper.get();
websocket.getClass();
map.from(websocket::getMaxFramePayloadLength).whenNonNull().to(requestUpgradeStrategy::setMaxFramePayloadLength);
websocket.getClass();
map.from(websocket::isProxyPing).to(requestUpgradeStrategy::setHandlePing);
return requestUpgradeStrategy;
}
}
}
GatewayHystrixCircuitBreakerAutoConfiguration类
熔断器实例化
routes:
- id: test
uri: lb://test
predicates:
- Path=//test/**
filters: //定义hystrix circuitbreaker
- name: CircuitBreaker
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback //定义fallback访问
uri: http://localhost:9994
predicates:
- Path=/fallback
filters:
- name: FallbackHeaders //fallbackHeader配置
args:
executionExceptionTypeHeaderName: Test-Header
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnProperty(
name = {"spring.cloud.gateway.enabled"},
matchIfMissing = true
)
@AutoConfigureAfter({ReactiveHystrixCircuitBreakerAutoConfiguration.class})
@ConditionalOnClass({DispatcherHandler.class, HystrixCircuitBreakerAutoConfiguration.class, ReactiveCircuitBreakerFactory.class, ReactiveHystrixCircuitBreakerFactory.class})
public class GatewayHystrixCircuitBreakerAutoConfiguration {
...
@Bean
@ConditionalOnBean({ReactiveHystrixCircuitBreakerFactory.class})
public SpringCloudCircuitBreakerHystrixFilterFactory springCloudCircuitBreakerHystrixFilterFactory(ReactiveHystrixCircuitBreakerFactory reactiveCircuitBreakerFactory, ObjectProvider<DispatcherHandler> dispatcherHandler) {
return new SpringCloudCircuitBreakerHystrixFilterFactory(reactiveCircuitBreakerFactory, dispatcherHandler);
}
@Bean
@ConditionalOnMissingBean({FallbackHeadersGatewayFilterFactory.class})
public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() {
return new FallbackHeadersGatewayFilterFactory();
}
}
GatewayLoadBalancerClientAutoConfiguration类
引入ribbon时的负载均衡实例化
当GatewayLoadBalancerClientAutoConfiguration条件互斥
LoadBalancerClientFilter与ReactiveLoadBalancerClientFilter只会实例化一个
routes:
- id: myRoute
uri: lb://servicetest
predicates:
- Path=/servicetest/**
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class, DispatcherHandler.class})
@AutoConfigureAfter({RibbonAutoConfiguration.class})
@EnableConfigurationProperties({LoadBalancerProperties.class})
public class GatewayLoadBalancerClientAutoConfiguration {
public GatewayLoadBalancerClientAutoConfiguration() {
}
@Bean
@ConditionalOnBean({LoadBalancerClient.class})
@ConditionalOnMissingBean({LoadBalancerClientFilter.class, ReactiveLoadBalancerClientFilter.class})
public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client, LoadBalancerProperties properties) {
return new LoadBalancerClientFilter(client, properties);
}
}
GatewayNoLoadBalancerClientAutoConfiguration类
没有引入ribbon时的负载均衡实例化
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingClass({"org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration"})
@ConditionalOnMissingBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerProperties.class})
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class})
public class GatewayNoLoadBalancerClientAutoConfiguration {
public GatewayNoLoadBalancerClientAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean({LoadBalancerClientFilter.class})
public GatewayNoLoadBalancerClientAutoConfiguration.NoLoadBalancerClientFilter noLoadBalancerClientFilter(LoadBalancerProperties properties) {
return new GatewayNoLoadBalancerClientAutoConfiguration.NoLoadBalancerClientFilter(properties.isUse404());
}
protected static class NoLoadBalancerClientFilter implements GlobalFilter, Ordered {
private final boolean use404;
public NoLoadBalancerClientFilter(boolean use404) {
this.use404 = use404;
}
public int getOrder() {
return 10100;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = (String)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
if (url != null && ("lb".equals(url.getScheme()) || "lb".equals(schemePrefix))) {
throw NotFoundException.create(this.use404, "Unable to find instance for " + url.getHost());
} else {
return chain.filter(exchange);
}
}
}
}
GatewayMetricsAutoConfiguration类
整合监控相关,提供监控指标。与actuators配合
url暴露在 /actuator/metrics/gateway.requests 端点中,也可以轻松与 Prometheus 整合,从而创建一个 Grafana dashboard。
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnProperty(
name = {"spring.cloud.gateway.enabled"},
matchIfMissing = true
)
@EnableConfigurationProperties({GatewayMetricsProperties.class})
@AutoConfigureBefore({HttpHandlerAutoConfiguration.class})
@AutoConfigureAfter({MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class})
@ConditionalOnClass({DispatcherHandler.class, MeterRegistry.class, MetricsAutoConfiguration.class})
public class GatewayMetricsAutoConfiguration {
...
@Bean
@ConditionalOnBean({MeterRegistry.class})
@ConditionalOnProperty(
name = {"spring.cloud.gateway.metrics.enabled"},
matchIfMissing = true
)
public GatewayMetricsFilter gatewayMetricFilter(MeterRegistry meterRegistry, List<GatewayTagsProvider> tagsProviders) {
return new GatewayMetricsFilter(meterRegistry, tagsProviders);
}
}
GatewayRedisAutoConfiguration类
与redies结合,可使用到限流等方面:
RedisRateLimiter+request_rate_limiter.lua脚本
@Configuration(
proxyBeanMethods = false
)
@AutoConfigureAfter({RedisReactiveAutoConfiguration.class})
@AutoConfigureBefore({GatewayAutoConfiguration.class})
@ConditionalOnBean({ReactiveRedisTemplate.class})
@ConditionalOnClass({RedisTemplate.class, DispatcherHandler.class})
class GatewayRedisAutoConfiguration {
GatewayRedisAutoConfiguration() {
}
@Bean
public RedisScript redisRequestRateLimiterScript() {
DefaultRedisScript redisScript = new DefaultRedisScript();
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/scripts/request_rate_limiter.lua")));
redisScript.setResultType(List.class);
return redisScript;
}
@Bean
@ConditionalOnMissingBean
public RedisRateLimiter redisRateLimiter(ReactiveStringRedisTemplate redisTemplate, @Qualifier("redisRequestRateLimiterScript") RedisScript<List<Long>> redisScript, ConfigurationService configurationService) {
return new RedisRateLimiter(redisTemplate, redisScript, configurationService);
}
}
GatewayDiscoveryClientAutoConfiguration类
spring.cloud.gateway.discovery.locator.enabled:是否与服务发现组件进行结合,通过 serviceId 转发到具体的服务实例。默认为false,设为true便开启通过服务中心的自动根据 serviceId 创建路由的功能。
与eureka\nacos配合使用
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnProperty(
name = {"spring.cloud.gateway.enabled"},
matchIfMissing = true
)
@AutoConfigureBefore({GatewayAutoConfiguration.class})
@AutoConfigureAfter({CompositeDiscoveryClientAutoConfiguration.class})
@ConditionalOnClass({DispatcherHandler.class})
@EnableConfigurationProperties
public class GatewayDiscoveryClientAutoConfiguration {
public GatewayDiscoveryClientAutoConfiguration() {
}
public static List<PredicateDefinition> initPredicates() {
ArrayList<PredicateDefinition> definitions = new ArrayList();
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName(NameUtils.normalizeRoutePredicateName(PathRoutePredicateFactory.class));
predicate.addArg("pattern", "'/'+serviceId+'/**'");
definitions.add(predicate);
return definitions;
}
public static List<FilterDefinition> initFilters() {
ArrayList<FilterDefinition> definitions = new ArrayList();
FilterDefinition filter = new FilterDefinition();
filter.setName(NameUtils.normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
String regex = "'/' + serviceId + '/(?<remaining>.*)'";
String replacement = "'/${remaining}'";
filter.addArg("regexp", regex);
filter.addArg("replacement", replacement);
definitions.add(filter);
return definitions;
}
//DiscoveryLocatorProperties实例化
@Bean
public DiscoveryLocatorProperties discoveryLocatorProperties() {
DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
properties.setPredicates(initPredicates());
properties.setFilters(initFilters());
return properties;
}
//开启通过服务中心的自动根据 serviceId 创建路由的功能
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnProperty(
value = {"spring.cloud.discovery.reactive.enabled"},
matchIfMissing = true
)
public static class ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration {
public ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration() {
}
@Bean
@ConditionalOnProperty(
name = {"spring.cloud.gateway.discovery.locator.enabled"}
)
public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
}
}
}
SimpleUrlHandlerMappingGlobalCorsAutoConfiguration类
spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping为ture时,支持cors-configurations配置,对CORS请求预检
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({SimpleUrlHandlerMapping.class})
@ConditionalOnProperty(
name = {"spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping"},
matchIfMissing = false
)
public class SimpleUrlHandlerMappingGlobalCorsAutoConfiguration {
...
//CORS请求预检
@PostConstruct
void config() {
this.simpleUrlHandlerMapping.setCorsConfigurations(this.globalCorsProperties.getCorsConfigurations());
}
}
GatewayReactiveLoadBalancerClientAutoConfiguration类
ReactiveLoadBalancerClientFilter实例化
LoadBalancerClientFilter与ReactiveLoadBalancerClientFilter只会实例化一个
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class, DispatcherHandler.class})
@AutoConfigureBefore({GatewayLoadBalancerClientAutoConfiguration.class})
@AutoConfigureAfter({LoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({LoadBalancerProperties.class})
public class GatewayReactiveLoadBalancerClientAutoConfiguration {
...
//ReactiveLoadBalancerClientFilter实例化
@Bean
@ConditionalOnBean({LoadBalancerClientFactory.class})
@ConditionalOnMissingBean({ReactiveLoadBalancerClientFilter.class})
@Conditional({GatewayReactiveLoadBalancerClientAutoConfiguration.OnNoRibbonDefaultCondition.class})
public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
return new ReactiveLoadBalancerClientFilter(clientFactory, properties);
}
private static final class OnNoRibbonDefaultCondition extends AnyNestedCondition {
private OnNoRibbonDefaultCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnMissingClass({"org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient"})
static class RibbonLoadBalancerNotPresent {
RibbonLoadBalancerNotPresent() {
}
}
@ConditionalOnProperty(
value = {"spring.cloud.loadbalancer.ribbon.enabled"},
havingValue = "false"
)
static class RibbonNotEnabled {
RibbonNotEnabled() {
}
}
}
}
路由配置加载
1、加载Route对象beans实例化
2、InMemoryRouteDefinitionRepository提供save/delete操作RouteDefinition(路由对象)
3、CompositeRouteDefinitionLocator提供将RouteDefinition加载到RouteDefinitionLocator
4、RouteDefinitionRouteLocator
提供将RouteDefinitionLocator/predicates/gatewayFilterFactories/gatewayProperties/configurationService对象加载到RouteDefinitionRouteLocator对象,并提供获取各对象的方法
5、CachingRouteLocator提供由applicatEventPublisther自动刷新对Route缓存
6、图解
调用链路解析
1、调用api接口,由于自定义Filter的order一般很小(如tokenFilter -100之类),请求从经过该类Filter
2、进入DispatcherHandler.handle()
DispathcerHandler适配器转发,把ServerHttpRequest/ServerHttpResponse转换成ServerWebExchange
3、进入RoutePredicateHandlerMapping
因为其order=1。AbstractHandlerMapping类中获取Handler时带参数ServerWebExchange调用RoutePredicateHandlerMapping.getHandlerInternal方法,通过lookupRoute()方法--》lookupRoute(exchange)
在lookupRoute(exchange)中如果匹配从exchange中gatewayPredicateRouteAttr设值为servieid
加入GatewayFilterChain
4、进入FilteringWebHandler根据GatewayFilterChain中Filters列表按order值从小到大,依次执行filters方法调用
其中:
4.1、LoadBalancerClientFilter负载均衡
当进入LoadBalancerClientFilter,由URI为lb:类型,通过this.loadBalancer.choose()(负载均衡LoadBalancerClient实例化)从获取接口服务,其getInstance()有三个实现(TraceFeignContext/SpringClientFactory/NamedContextFactory),当前是从
NamedContextFactory实现getInstance方法,接口服务实例根据serviceid从AnnotationConfigApplicationContext中获取 (说明服务实例已被NacosServer加载到AnnotationConfigApplicationContext)
4.2、ForwardRoutingFilter
最后进入ForwardRoutingFilter,当有forward时,还是通过DispatcherHandler().handle(exchange)到真实的接口服务
5、图解
1、
2、RoutePredicateHandlerMapping
3、ForwardPathFilter
4、当失败时,交由AbstractHanderMapping转发到当前 接口
RouteDefinition类
@Validated
public class RouteDefinition {
private String id;
@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList();
@Valid
private List<FilterDefinition> filters = new ArrayList();
@NotNull
private URI uri;
private Map<String, Object> metadata = new HashMap();
private int order = 0;
RouteDefinitionRouteLocator类
public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
public static final String DEFAULT_FILTERS = "defaultFilters";
protected final Log logger = LogFactory.getLog(this.getClass());
private final RouteDefinitionLocator routeDefinitionLocator;
private final ConfigurationService configurationService;
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap();
private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap();
private final GatewayProperties gatewayProperties;
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List<RoutePredicateFactory> predicates, List<GatewayFilterFactory> gatewayFilterFactories, GatewayProperties gatewayProperties, ConfigurationService configurationService) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.configurationService = configurationService;
this.initFactories(predicates);
gatewayFilterFactories.forEach((factory) -> {
GatewayFilterFactory var10000 = (GatewayFilterFactory)this.gatewayFilterFactories.put(factory.name(), factory);
});
this.gatewayProperties = gatewayProperties;
}
...
private void initFactories(List<RoutePredicateFactory> predicates) {
predicates.forEach((factory) -> {
String key = factory.name();
if (this.predicates.containsKey(key)) {
this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: " + this.predicates.get(key) + ". It will be overwritten.");
}
this.predicates.put(key, factory);
if (this.logger.isInfoEnabled()) {
this.logger.info("Loaded RoutePredicateFactory [" + key + "]");
}
});
}
public Flux<Route> getRoutes() {
Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute);
if (!this.gatewayProperties.isFailOnRouteDefinitionError()) {
routes = routes.onErrorContinue((error, obj) -> {
if (this.logger.isWarnEnabled()) {
this.logger.warn("RouteDefinition id " + ((RouteDefinition)obj).getId() + " will be ignored. Definition has invalid configs, " + error.getMessage());
}
});
}
return routes.map((route) -> {
if (this.logger.isDebugEnabled()) {
this.logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
private Route convertToRoute(RouteDefinition routeDefinition) {
AsyncPredicate<ServerWebExchange> predicate = this.combinePredicates(routeDefinition);
List<GatewayFilter> gatewayFilters = this.getFilters(routeDefinition);
return ((AsyncBuilder)Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters)).build();
}
List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
ArrayList<GatewayFilter> ordered = new ArrayList(filterDefinitions.size());
for(int i = 0; i < filterDefinitions.size(); ++i) {
FilterDefinition definition = (FilterDefinition)filterDefinitions.get(i);
GatewayFilterFactory factory = (GatewayFilterFactory)this.gatewayFilterFactories.get(definition.getName());
if (factory == null) {
throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("RouteDefinition " + id + " applying filter " + definition.getArgs() + " to " + definition.getName());
}
Object configuration = this.configurationService.with(factory).name(definition.getName()).properties(definition.getArgs()).eventFunction((bound, properties) -> {
return new FilterArgsEvent(this, id, (Map)properties);
}).bind();
if (configuration instanceof HasRouteId) {
HasRouteId hasRouteId = (HasRouteId)configuration;
hasRouteId.setRouteId(id);
}
GatewayFilter gatewayFilter = factory.apply(configuration);
if (gatewayFilter instanceof Ordered) {
ordered.add(gatewayFilter);
} else {
ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
}
}
return ordered;
}
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
List<GatewayFilter> filters = new ArrayList();
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
filters.addAll(this.loadGatewayFilters("defaultFilters", new ArrayList(this.gatewayProperties.getDefaultFilters())));
}
if (!routeDefinition.getFilters().isEmpty()) {
filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), new ArrayList(routeDefinition.getFilters())));
}
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
private AsyncPredicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
AsyncPredicate<ServerWebExchange> predicate = this.lookup(routeDefinition, (PredicateDefinition)predicates.get(0));
AsyncPredicate found;
for(Iterator var4 = predicates.subList(1, predicates.size()).iterator(); var4.hasNext(); predicate = predicate.and(found)) {
PredicateDefinition andPredicate = (PredicateDefinition)var4.next();
found = this.lookup(routeDefinition, andPredicate);
}
return predicate;
}
private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
RoutePredicateFactory<Object> factory = (RoutePredicateFactory)this.predicates.get(predicate.getName());
if (factory == null) {
throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("RouteDefinition " + route.getId() + " applying " + predicate.getArgs() + " to " + predicate.getName());
}
Object config = ((ConfigurableBuilder)((ConfigurableBuilder)((ConfigurableBuilder)this.configurationService.with(factory).name(predicate.getName())).properties(predicate.getArgs())).eventFunction((bound, properties) -> {
return new PredicateArgsEvent(this, route.getId(), properties);
})).bind();
return factory.applyAsync(config);
}
}
}
RoutePredicateHandlerMapping类
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private final FilteringWebHandler webHandler;
private final RouteLocator routeLocator;
private final Integer managementPort;
private final RoutePredicateHandlerMapping.ManagementPortType managementPortType;
public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
this.webHandler = webHandler;
this.routeLocator = routeLocator;
this.managementPort = getPortProperty(environment, "management.server.");
this.managementPortType = this.getManagementPortType(environment);
this.setOrder(1);
this.setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
}
...
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
if (this.managementPortType == RoutePredicateHandlerMapping.ManagementPortType.DIFFERENT && this.managementPort != null && exchange.getRequest().getURI().getPort() == this.managementPort) {
return Mono.empty();
} else {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR, this.getSimpleName());
return this.lookupRoute(exchange).flatMap((r) -> {
exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Mapping [" + this.getExchangeDesc(exchange) + "] to " + r);
}
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r);
return Mono.just(this.webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No RouteDefinition found for [" + this.getExchangeDesc(exchange) + "]");
}
})));
}
}
protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) {
return super.getCorsConfiguration(handler, exchange);
}
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes().concatMap((route) -> {
return Mono.just(route).filterWhen((r) -> {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return (Publisher)r.getPredicate().apply(exchange);
}).doOnError((e) -> {
this.logger.error("Error applying predicate for route: " + route.getId(), e);
}).onErrorResume((e) -> {
return Mono.empty();
});
}).next().map((route) -> {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Route matched: " + route.getId());
}
this.validateRoute(route, exchange);
return route;
});
}
}
LoadBalancerClientFilter类
该类后续不推介使用,在3.0.0版本中已由ReactiveLoadBalancerClientFilter替代
public class LoadBalancerClientFilter implements GlobalFilter, Ordered {
public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100;
private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class);
protected final LoadBalancerClient loadBalancer;
private LoadBalancerProperties properties;
public int getOrder() {
return 10100;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = (String)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
if (url != null && ("lb".equals(url.getScheme()) || "lb".equals(schemePrefix))) {
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);
if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url before: " + url);
}
ServiceInstance instance = this.choose(exchange);
if (instance == null) {
throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());
} else {
URI uri = exchange.getRequest().getURI();
String overrideScheme = instance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
URI requestUrl = this.loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);
if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
}
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange);
}
} else {
return chain.filter(exchange);
}
}
protected ServiceInstance choose(ServerWebExchange exchange) {
return this.loadBalancer.choose(((URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR)).getHost());
}
}
Exchange 标记 Routed
通过以上所有内置的全局过滤器后,不同协议的请求会由不同的过滤器转发到下游。
当一个请求走完整条过滤器链后,负责转发请求到下游的那个过滤器会在 exchange 中添加一个 gatewayAlreadyRouted 属性,从而将 exchange 标记为 routed(已路由)。一旦请求被标记为 routed ,其他路由过滤器将不会再次路由该请求,而是直接跳过。负责添加这个gatewayAlreadyRouted 属性的过滤器就是最终负责转发请求的过滤器:
- http、https请求会由NettyRoutingFilter或WebClientHttpRoutingFilter添加这个属性
- forward请求会由ForwardRoutingFilter添加这个属性
- websocket请求会由WebsocketRoutingFilter添加这个属性
- 这些过滤器调用了以下方法将 exchange 标记为 routed ,或检查 exchange 是否是 routed:
- ServerWebExchangeUtils.isAlreadyRouted:检查exchange是否为routed状态
- ServerWebExchangeUtils.setAlreadyRouted:将exchange设置为routed状态
总结,当Gateway通过 gatewayAlreadyRouted 属性表示这个请求已经转发过了,而无需其他过滤器重复路由,从而防止重复的路由转发。