前言:
我们知道 Spring Cloud Gateway 是一个高性能网关,具备负载均衡的能力,那你知道 Gateway 的负载均衡是怎么实现的吗?本篇我们来分析一下 Spring Cloud Gateway 的负载均衡的实现。
Spring Cloud Gateway 的负载均衡实现
我们在阅读源码的时候常常会去看 META-INF 下的 spring.factories,我们去该文件下看看能不能找到一点更负责均衡相关的代码,spring.factories 文件内容如下:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration,\
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.cloud.gateway.config.GatewayReactiveOAuth2AutoConfiguration
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.gateway.config.GatewayEnvironmentPostProcessor
在 spring.factories 文件中我们看到了 GatewayNoLoadBalancerClientAutoConfiguration 和 GatewayReactiveLoadBalancerClientAutoConfiguration 这两个配置类,这两个配置类中都有 LoadBalancer 关键字,大胆猜测这两个配置类和 Gateway 的负载均衡有关系。
GatewayNoLoadBalancerClientAutoConfiguration 源码解析
GatewayNoLoadBalancerClientAutoConfiguration 直译是网关无负载均衡器客户端自动配置类,该类的实际作用是当 Gateway 转发的目标 url 中配置的是lb 约束时抛出NotFoundException异常。
//网关无负载均衡器客户端自动配置类
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingClass({"org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer"})
@ConditionalOnMissingBean({ReactiveLoadBalancer.class})
@EnableConfigurationProperties({GatewayLoadBalancerProperties.class})
@AutoConfigureAfter({GatewayReactiveLoadBalancerClientAutoConfiguration.class})
public class GatewayNoLoadBalancerClientAutoConfiguration {
public GatewayNoLoadBalancerClientAutoConfiguration() {
}
//初始化 NoLoadBalancerClientFilter bean
@Bean
@ConditionalOnMissingBean({ReactiveLoadBalancerClientFilter.class})
public GatewayNoLoadBalancerClientAutoConfiguration.NoLoadBalancerClientFilter noLoadBalancerClientFilter(GatewayLoadBalancerProperties properties) {
return new GatewayNoLoadBalancerClientAutoConfiguration.NoLoadBalancerClientFilter(properties.isUse404());
}
//内部类 NoLoadBalancerClientFilter
protected static class NoLoadBalancerClientFilter implements GlobalFilter, Ordered {
private final boolean use404;
public NoLoadBalancerClientFilter(boolean use404) {
this.use404 = use404;
}
public int getOrder() {
return 10150;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//获取 url
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))) {
//创建 404 错误 默认 503 错误
throw NotFoundException.create(this.use404, "Unable to find instance for " + url.getHost());
} else {
//继续执行过滤器
return chain.filter(exchange);
}
}
}
}
GatewayReactiveLoadBalancerClientAutoConfiguration 类源码解析
GatewayReactiveLoadBalancerClientAutoConfiguration 作为一个配置类,作用很简单就是注入了 ReactiveLoadBalancerClientFilter 全局过滤器。
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class, DispatcherHandler.class})
@AutoConfigureAfter({LoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({GatewayLoadBalancerProperties.class})
public class GatewayReactiveLoadBalancerClientAutoConfiguration {
public GatewayReactiveLoadBalancerClientAutoConfiguration() {
}
@Bean
@ConditionalOnBean({LoadBalancerClientFactory.class})
@ConditionalOnMissingBean({ReactiveLoadBalancerClientFilter.class})
@ConditionalOnEnabledGlobalFilter
public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, GatewayLoadBalancerProperties properties, LoadBalancerProperties loadBalancerProperties) {
return new ReactiveLoadBalancerClientFilter(clientFactory, properties, loadBalancerProperties);
}
}
ReactiveLoadBalancerClientFilter 源码解析
ReactiveLoadBalancerClientFilter 是一个响应式全局负载均衡过滤器,是 Spring Cloud Gateway 高版本使用的负载均衡过滤器,Spring Cloud Gateway 低版本使用的是 LoadBalancerClientFilter,LoadBalancerClientFilter 使用的是 Ribbon 的阻塞式负责均衡客户端(Ribbon 在 Spring Cloud Gateway 高版本中已经被移出了)。
ReactiveLoadBalancerClientFilter#filter 方法源码解析
ReactiveLoadBalancerClientFilter#filter 是 Gateway 完成负载均衡的方法 ,主要有以下逻辑:
- 获取请求的 URI,这个 URI 是带有微服务id 的URI,例如:lb://user-service/query-user。
- 判断获取到的 URI 是否是以 lb 打头,如果是则从服务中获取服务id,如果不是则执行下一个过滤器。
- 获取负载均衡处理器,调用 choose 方法。
- 判断 response 中是否有服务实例信息。
- 从 response 对象中获取服务实例信息,创建委托服务实例,重建请求 url。
- 将重建后的 url 信息存储到 exchange 中。
//org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter#filter
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//获取请求的 url 例如:lb://user-service/query-user
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))) {
//url 不为空 且 是以 lb 开头
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);
if (log.isTraceEnabled()) {
log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);
}
//获取请求 uri
URI requestUri = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
//从请求中 获取 serviceId
String serviceId = requestUri.getHost();
//获取负责均衡处理器
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors(this.clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class), RequestDataContext.class, ResponseData.class, ServiceInstance.class);
DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest(new RequestDataContext(new RequestData(exchange.getRequest()), this.getHint(serviceId, this.loadBalancerProperties.getHint())));
return this.choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext((response) -> {
if (!response.hasServer()) {
//没有服务实例信息
supportedLifecycleProcessors.forEach((lifecycle) -> {
lifecycle.onComplete(new CompletionContext(Status.DISCARD, lbRequest, response));
});
throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());
} else {
//服务实例信息 ip 服务名
ServiceInstance retrievedInstance = (ServiceInstance)response.getServer();
//获取 uri 例如:http://localhost:8888/query-user
URI uri = exchange.getRequest().getURI();
//http https 判断
String overrideScheme = retrievedInstance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
//创建委托服务实例
DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(retrievedInstance, overrideScheme);
//重建 uri 其实已经是服务实例的 ip+url 了 例如 http://192.168.123.132:8092/query-user
URI requestUrl = this.reconstructURI(serviceInstance, uri);
if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
}
//存储 requestUrl
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
//存储 response
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR, response);
supportedLifecycleProcessors.forEach((lifecycle) -> {
//创建计数器 负载均衡策略使用
lifecycle.onStartRequest(lbRequest, response);
});
}
}).then(chain.filter(exchange)).doOnError((throwable) -> {
supportedLifecycleProcessors.forEach((lifecycle) -> {
lifecycle.onComplete(new CompletionContext(Status.FAILED, throwable, lbRequest, (Response)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR)));
});
}).doOnSuccess((aVoid) -> {
supportedLifecycleProcessors.forEach((lifecycle) -> {
lifecycle.onComplete(new CompletionContext(Status.SUCCESS, lbRequest, (Response)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR), new ResponseData(exchange.getResponse(), new RequestData(exchange.getRequest()))));
});
});
} else {
return chain.filter(exchange);
}
}
//org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter#choose
private Mono<Response<ServiceInstance>> choose(Request<RequestDataContext> lbRequest, String serviceId, Set<LoadBalancerLifecycle> supportedLifecycleProcessors) {
//获取负责均衡器
ReactorLoadBalancer<ServiceInstance> loadBalancer = (ReactorLoadBalancer)this.clientFactory.getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
if (loadBalancer == null) {
//为空判断
throw new NotFoundException("No loadbalancer available for " + serviceId);
} else {
supportedLifecycleProcessors.forEach((lifecycle) -> {
lifecycle.onStart(lbRequest);
});
return loadBalancer.choose(lbRequest);
}
}
//org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#choose
public Mono<Response<ServiceInstance>> choose(Request request) {
//获取服务实例列表
ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next().map((serviceInstances) -> {
return this.processInstanceResponse(supplier, serviceInstances);
});
}
//org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#processInstanceResponse
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
//服务实例响应
Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
}
//返回服务实例响应
return serviceInstanceResponse;
}
Spring Cloud Gateway 默认使用轮训的负载均衡策略。
如有不正确的地方请各位指出纠正。