SpringCloud.Hoxton 版本, 全新负载均衡器Loadbalancer

SpringCloud.Hoxton 版本, 全新负载均衡器Loadbalancer

前置说明

源码分析的版本为Honxton.Release. 为什么选这个版本呢? 是因为springcloud在这个版本才加入自己的负载均衡器. 不过springcloud为了兼容性,会在ribbon依赖引入时, 优先使用ribbon.
主要的依赖, 也可以直接下载我之前的 sample-springcloud 项目, 里面有 h版本的分支demo, 链接放到评论区

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

初步分析

首先这个是在H版本才出现, 这个组件的目的根据官方文档所说就是: 提供自己而定客户端一遍负载均衡器抽象和实现. 主要是额外增加响应式的负载均衡器接口以及默认 的轮询实现(从这可以看出, 其负载均衡策略目前只有轮询, 所以暂时不建议从ribbon切换过来, 毕竟ribbon的负载均衡策略还是很丰富的).
那么, 一个负载均衡器怎么自动装配的肯定是我们需要关注的, 以及它服务列表来源, 以及怎么负载均衡和什么时候进行负载均衡. 接下来我们就这三个问题进行分析.

负载均衡器的自动装配

(本次源码基于阻塞式的客户端)在负载均衡器的自动装配类中, 主要是这三个 common/LoadBalancerAutoConfiguration , loadbalancer/LoadBalancerAutoConfiguration , BlockingLoadBalancerClientAutoConfiguration

那么我们一个个分析, 首先是common下的LoadBalancerAutoConfiguration自动配置类, 这个类的主要作用是提供LoadBalancerRequestFactory, 这个主要是用于生产LoadBalancerRequest,这个类具体的作用在下面分析

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

	// 提供工厂类
	@Bean
	@ConditionalOnMissingBean
	public LoadBalancerRequestFactory loadBalancerRequestFactory(
			LoadBalancerClient loadBalancerClient) {
		return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {
		// 提供resttemplate 拦截器, 这里的方法名请忽略ribbon, 因为这是commons包, 不可能和任何的实现有关系, 而且也没有和ribbon相关的条件装配, 应该是springcloud忘记改名字了. 
		// 这个类的作用就是连接restrtemplate 和 loadBalancerClient的纽带
		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

		// 将拦截器应用到RestTemplate中去
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}
.... 其他方法省略
}


// 这个接口就是一个请求动作
public interface LoadBalancerRequest<T> {
	T apply(ServiceInstance instance) throws Exception;
}

// LoadBalancerRequest 的工厂
public class LoadBalancerRequestFactory {
	public LoadBalancerRequest<ClientHttpResponse> createRequest(
			final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) {
		return instance -> {
			HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance,
					this.loadBalancer);
			if (this.transformers != null) {
				for (LoadBalancerRequestTransformer transformer : this.transformers) {
					serviceRequest = transformer.transformRequest(serviceRequest,
							instance);
				}
			}
			return execution.execute(serviceRequest, body); //具体的请求执行, 因为这里涉及restTemplate的源码了, 所以不在本文范围
		};
	}

}


接下来是loadbalancer依赖下的自动配置类, LoadBalancerAutoConfiguration ,
,BlockingLoadBalancerClientAutoConfiguration, 前一个的作用是提供LoadBalancerClientFactory, 后一个是为了提供BlockingLoadBalancerClient


@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@AutoConfigureBefore({ ReactorLoadBalancerClientAutoConfiguration.class,
		LoadBalancerBeanPostProcessorAutoConfiguration.class,
		ReactiveLoadBalancerAutoConfiguration.class })
public class LoadBalancerAutoConfiguration {

	// 提供ReactiveLoadBalancer 负载均衡器
	@Bean
	public LoadBalancerClientFactory loadBalancerClientFactory() {
		LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();
		clientFactory.setConfigurations(
				this.configurations.getIfAvailable(Collections::emptyList));
		return clientFactory;
	}

}

	public class LoadBalancerClientFactory
		extends NamedContextFactory<LoadBalancerClientSpecification>
		implements ReactiveLoadBalancer.Factory<ServiceInstance> {
	
	// 如果自己设置了@LoadBalancerClient, 那么会从对应的子上下文中去获取, 否则就走defautl的配置
	@Override
	public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
		return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
	}

}


@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
@AutoConfigureBefore({
		org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.class,
		AsyncLoadBalancerAutoConfiguration.class })
public class BlockingLoadBalancerClientAutoConfiguration {
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(RestTemplate.class)
	@Conditional(OnNoRibbonDefaultCondition.class)
	protected static class BlockingLoadbalancerClientConfig {
	// 提供一个负载均衡的客户端
		@Bean
		@ConditionalOnBean(LoadBalancerClientFactory.class)
		@Primary
		public BlockingLoadBalancerClient blockingLoadBalancerClient(
				LoadBalancerClientFactory loadBalancerClientFactory) {
				// loadBalancerClientFactory
			return new BlockingLoadBalancerClient(loadBalancerClientFactory);
		}

	}

// 阻塞的负载均衡客户端
public class BlockingLoadBalancerClient implements LoadBalancerClient {
	private final LoadBalancerClientFactory loadBalancerClientFactory;

	@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
			throws IOException {
			// 获取serviceId 对应的服务实例
		ServiceInstance serviceInstance = choose(serviceId);
		if (serviceInstance == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		return execute(serviceId, serviceInstance, request);
	}

	@Override
	public <T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException {
		try {
			return request.apply(serviceInstance); // 回调, 这个在上面讲过了, 其实就是调用restTemplate的方法去请求远程了
		}
		catch (IOException iOException) {
			throw iOException;
		}
		catch (Exception exception) {
			ReflectionUtils.rethrowRuntimeException(exception);
		}
		return null;
	}


	@Override
	public ServiceInstance choose(String serviceId) {
	// 获取负载均衡器
		ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory
				.getInstance(serviceId);
		if (loadBalancer == null) {
			return null;
		}
		Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose())
				.block();
		if (loadBalancerResponse == null) {
			return null;
		}
		return loadBalancerResponse.getServer();
	}

}


springcloud实现的自动配置类真的是太长了 虽然我们用的时候很简单, 但是其实每个配置, 每个依赖都会影响自动装配的bean是哪个, 所以这里面的一些bean需要十分的了解 , 后面会对上面的各种bean进行串联分析.

服务列表的获取以及负载均衡的实现

从上面的一大堆装配的类, 那么其中提供服务列表的类就是ServiceInstanceListSupplier ,这个类其实是在LoadBalancerClientConfiguration这个类中提供的, 而这个类其实是通过 @LoadBalancerClient注解进行导入的, 所以在上面自动配置类里面没有发现这个配置类.

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {

@Configuration(proxyBeanMethods = false)
	@ConditionalOnBlockingDiscoveryEnabled
	@Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER + 1)
	public static class BlockingSupportConfiguration {

		@Bean
		@ConditionalOnBean(DiscoveryClient.class)
		@ConditionalOnMissingBean
		public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
				// 这个接口,我想看过我前一篇文章的人应该比较熟悉了,就是获取服务列表的springcloud的抽象接口
				DiscoveryClient discoveryClient, Environment env,
				ApplicationContext context) {
			DiscoveryClientServiceInstanceListSupplier delegate = new DiscoveryClientServiceInstanceListSupplier(
					discoveryClient, env);
			ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
					.getBeanProvider(LoadBalancerCacheManager.class);
			if (cacheManagerProvider.getIfAvailable() != null) {
				return new CachingServiceInstanceListSupplier(delegate,
						cacheManagerProvider.getIfAvailable());
			}
			return delegate;
		}
	}
}

所以服务列表的获取就是通过DiscoveryClient(具体实现可以是EurekaDiscoveryClient), 那么具体的负载实现在哪个类实现呢?其实也是在上面的配置类中有的RoundRobinLoadBalancer


public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
	private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
	// 负载均衡
	public Mono<Response<ServiceInstance>> choose(Request request) {
	
		if (serviceInstanceListSupplierProvider != null) {
		// 这个就是上面提供到服务实例列表提供者, 这里调用它的get进行获取
			ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
					.getIfAvailable(NoopServiceInstanceListSupplier::new);
					
			return  supplier.get() // 调用get,下面会讲到
			.next()  // 获取第一个
			.map(this::getInstanceResponse); // 将服务列表转换为一个实例,也就是调用取模方法
		}
		ServiceInstanceSupplier supplier = this.serviceInstanceSupplier
				.getIfAvailable(NoopServiceInstanceSupplier::new);
		return supplier.get().collectList().map(this::getInstanceResponse);
	}
	
	// 取模获取一个服务实例
	private Response<ServiceInstance> getInstanceResponse(
			List<ServiceInstance> instances) {
		if (instances.isEmpty()) {
		
			return new EmptyResponse();
		}
	
		int pos = Math.abs(this.position.incrementAndGet());
			// 取模进行轮询
		ServiceInstance instance = instances.get(pos % instances.size());

		return new DefaultResponse(instance);
	}
}

// 服务列表获取实现
public class DiscoveryClientServiceInstanceListSupplier
		implements ServiceInstanceListSupplier {

	private final String serviceId;

	private final Flux<ServiceInstance> serviceInstances;

	public DiscoveryClientServiceInstanceListSupplier(DiscoveryClient delegate,
			Environment environment) {
		this.serviceId = environment.getProperty(PROPERTY_NAME);
		this.serviceInstances = Flux
				.defer(() ->  Flux.fromIterable(delegate.getInstances(serviceId))) //defer是延迟到订阅时才加载, fromIterable就是通过discoveryClient去获取对应serviceId的服务实例列表
				.subscribeOn(Schedulers.boundedElastic());
	}

	public DiscoveryClientServiceInstanceListSupplier(ReactiveDiscoveryClient delegate,
			Environment environment) {
		this.serviceId = environment.getProperty(PROPERTY_NAME);
		this.serviceInstances = delegate.getInstances(serviceId);
	}
	// 这里就是get的实现
	@Override
	public Flux<List<ServiceInstance>> get() {
		return serviceInstances.collectList()//这里主要是将已发射的对象变成list
		.flux(); // 这里就是转换mono转换 flux
// 这里需要说明一下, 看上去这里好像做了多余的操作,就是将delegate.getInstances(serviceId)获取的list一直转来转去, 
// 其实是因为当前分析的是阻塞的服务提供列表, 如果是响应式负载均衡器, 那么服务端就不会一次性发送过来, 而是可能一下发2,3个一下2,3个, 
// 所以这里在获取到2,3后就封装成一个list, 供后去的负载均衡使用. 
// 想了解响应式的服务发现客户端, 具体可以看一下`ReactiveDiscoveryClient`这个接口和对应的实现类
	}

}

总结

其实到这基本讲完了, 最后再来理一下思路
首先是服务列表来源 ,主要是DiscoveryClientServiceInstanceListSupplier这个类的get方法, 负载均衡算法是在RoundRobinLoadBalancer的getInstanceResponse方法, 然后和RestTemplate结合的是通过LoadBalancerInterceptor, 这些就是核心的类了.
分析就到这了, 可能讲的有点乱, 不过大体的思路已经给出, 然后后面会补个uml图, 希望读者有发现我讲的有问题可以在下面评论提出,我会虚心接受的.

Loadbalancer自动配置类

和ribbon的对比

ribbon 的服务列表 DomainExtractingServerList,
ribbon 的负载均衡客户端 RibbonLoadBalancerClient
ribbon 的负载均衡器 ILoadBalancer 规则 IRule

其实可以看到负载均衡的实现都大相径庭, 不过目前Loadbalancer的负载均衡算法只有轮询,所以可以再等等,或者自己去实现丰富的负载均衡算法. 不过ribbon不好的地方在于ribbon自己也会维护一个服务列表, 这样相当于eureka client维护一套服务列表, ribbon维护一套, 那么这样服务下线后, ribbon的服务列表刷新的时间会很久, 因为无论是eureka还是ribbon都是定时去刷新的; 而Loadbalancer的实现,通过DiscoveryClientServiceInstanceListSupplier,直接将discoveryClient封装进去了,所以相对来说一致性会高很多

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值