SpringCloud.Honxton 版本, 全新負載均衡器Loadbalancer

SpringCloud.Honxton 版本, 全新負載均衡器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封裝進去了,所以相對來說一致性會高很多

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章