源码分析的版本为Honxton.Release. 为什么选这个版本呢? 是因为springcloud在这个版本才加入自己的负载均衡器. 不过springcloud为了兼容性,会在ribbon依赖引入时, 优先使用ribbon.
主要的依赖, 也可以直接下载我之前的 sample-springcloud 项目, 里面有 h版本的分支demo, 链接放到评论区
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
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图, 希望读者有发现我讲的有问题可以在下面评论提出,我会虚心接受的.
ribbon 的服务列表 DomainExtractingServerList
,
ribbon 的负载均衡客户端 RibbonLoadBalancerClient
ribbon 的负载均衡器 ILoadBalancer
规则 IRule
其实可以看到负载均衡的实现都大相径庭, 不过目前Loadbalancer的负载均衡算法只有轮询,所以可以再等等,或者自己去实现丰富的负载均衡算法. 不过ribbon不好的地方在于ribbon自己也会维护一个服务列表, 这样相当于eureka client维护一套服务列表, ribbon维护一套, 那么这样服务下线后, ribbon的服务列表刷新的时间会很久, 因为无论是eureka还是ribbon都是定时去刷新的; 而Loadbalancer的实现,通过DiscoveryClientServiceInstanceListSupplier
,直接将discoveryClient封装进去了,所以相对来说一致性会高很多