1:什么是Ribbon
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现.....
2:@LoadBalanced注解的原理
1: 为什么在RestTemplate上加上该注解就可以实现负载均衡
-
Ribbon底层还是要依赖于RestTemplate进行通信,这毋庸置疑。既然要让请求能够达到负载均衡的效果,那么就必须要对请求做一些特别的处理。那么拦截器 过滤器 就可以很好的实现这个效果。
-
拦截器要拦截RestTemplate发起的请求,在此之前,例如可以拿到所有的实例A的服务,然后根据负载均衡策略选择其中一个服务,再进行对其调用
2:在没有Ribbon之前的调用,服务A直接通过url地址直接访问
3:Ribbon调用过程
2:所以要搞懂Ribbon的原理就有几个点需要搞清楚
-
2.1:拦截器在什么时候设置的
-
2.2:如何选择一个负载均衡策略
-
2.3:如何从注册中心获取所有服务实例列表
3: 拦截器在什么时候设置的
根据SpringBoot自动装配的原理,我们找到该配置类
在该配置上有有一行注解 @AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class}),我们进入到LoadBalancerAutoConfiguration.class中
restTemplates 是所有被 @LoadBalanced 注解标记的 RestTemplate的集合
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
开始设置拦截器
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
//注入一个拦截器的Bean
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
//遍历所有被@LoadBalanced标记的RestTemnplate的集合,然后遍历,为每一个
//RestTemplate都设置一个LoadBalancerInterceptor的拦截器,就是上面这个拦截器
//所以在RestTemplate在执行的时候,就会经过一系列的拦截器处理,包括LoadBalancerInterceptor
//这个拦截器
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
所以在RestTemplate发起请求的时候会经过LoadBalancerInterceptor这个拦截器处理,也就是执行其中的 intercept() 方法
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
//拿到请求的URL
final URI originalUri = request.getURI();
//拿到服务名
String serviceName = originalUri.getHost();
Assert.state(serviceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
//执行请求
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
//选择一个负载均衡策略
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
//根据负载均衡策略选择一个服务
Server server = this.getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
//最后发起调用
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
}
4: 如何选择一个负载均衡组件
//该方法就是获取一个负载均衡策略的组件
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
但是你会发现,一直跟进源码确实直接调用Spring底层的 getBean() 方法直接获取一个负载均衡策略的组件,所以在此之前,一定是将负载均衡策略相关的Bean已经注入到 Spring容器中了 具体的实现就在 org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration.class中
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
return this.propertiesFactory.get(ILoadBalancer.class, config, name);
}
//没有配置就使用默认的负载均衡组件
return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}
然后根据不同的负载均衡策略选择一个服务
//根据负载均衡策略选择一个服务
Server server = this.getServer(loadBalancer, hint);
//最后根据不同的IRule规则选择其中的一个服务返回
rule.choose(key);
5: 如何从注册中心获取所有服务实例列表
记得之前默认加载了一个new ZoneAwareLoadBalancer()的负载均衡组件,在该类的构造方法中调用了父类 DynamicServerListLoadBalancer.class的构造函数
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList<T> serverList, ServerListFilter<T> filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping);
this.serverListImpl = serverList;
this.filter = filter;
this.serverListUpdater = serverListUpdater;
if (filter instanceof AbstractServerListFilter) {
((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
}
//从注册中心拿到服务列表的集合
restOfInit(clientConfig);
}
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
this.setEnablePrimingConnections(false);
//会开启一个定时线程池,定时去注册中心获取最新的服务列表
enableAndInitLearnNewServersFeature();
//初始化本地服务列表缓存
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
public void enableAndInitLearnNewServersFeature() {
LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
//进入
serverListUpdater.start(updateAction);
}
@Override
public synchronized void start(final UpdateAction updateAction) {
if (isActive.compareAndSet(false, true)) {
final Runnable wrapperRunnable = new Runnable() {
@Override
public void run() {
if (!isActive.get()) {
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
return;
}
try {
//去注册中心获取最新的服务列表信息
updateAction.doUpdate();
lastUpdated = System.currentTimeMillis();
} catch (Exception e) {
logger.warn("Failed one update cycle", e);
}
}
};
//每隔一段时间就会执行updateAction.doUpdate();方法去更新本地服务列表缓存
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
wrapperRunnable,
initialDelayMs,
refreshIntervalMs,
TimeUnit.MILLISECONDS
);
} else {
logger.info("Already active, no-op");
}
}
updateAction.doUpdate();
public void updateListOfServers() {
List<T> servers = new ArrayList();
if (this.serverListImpl != null) {
//从注册中心拿到所有的服务实例
servers = this.serverListImpl.getUpdatedListOfServers();
LOGGER.debug("List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);
if (this.filter != null) {
servers = this.filter.getFilteredListOfServers((List)servers);
LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);
}
}
//更新本地缓存
this.updateAllServerList((List)servers);
}
6:总结
- 1:Ribbon会拿到所有被@LoadBalanced标注的RestTemplate集合
- 2:会给每一个RestTemplate设置一个拦截器
- 3:在拦截器中Ribbon会获取所有注册实例
- 4:根据负载均衡策略选择一个服实例
- 5:拿到服务的IP+端口,最后通过RestTemplate发起服务调用