Spring Cloud Gateway 负载均衡源码分析

前言:

我们知道 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 默认使用轮训的负载均衡策略。

如有不正确的地方请各位指出纠正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值