Ribbon源码
主要功能分析:
Ribbon的负载均衡主要通过LoadBalancerClient来实现的,而LoadBalanceClient具体交给了ILoadBalancer来处理,ILoadBalancer通过配置IRule,IPing等,向EurekaClient获取注册列表的信息,默认每10秒向EurekaClient发送一次,进而检查是否需要更新服务的注册列表信息,最后,在得到服务注册列表后,ILoadBalancer根据IRule的策略进行负载均衡。
而RestTemplate加上@LoadBalance注解后,在远程调用时能负载均衡,主要是维护了一个被@LoadBalance注解的RestTemplate列表,并给该列表中的RestTemplate对象添加了拦截器,在拦截器的方法中,将远程调度方法交给了Ribbon的负载器LoadBalanceClient去处理,从而实现。
LoadBalanceClient继承了ServiceInstanceChooser,具体实现类为RibbonLoadBalanceClient。
public interface LoadBalancerClient extends ServiceInstanceChooser {
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException; URI reconstructURI(ServiceInstance instance, URI original);
}
public interface ServiceInstanceChooser { /**
* Chooses a ServiceInstance from the LoadBalancer for the specified service.
* @param serviceId The service ID to look up the LoadBalancer.
* @return A ServiceInstance that matches the serviceId.
*/
ServiceInstance choose(String serviceId);
}
reconstructURI用于重构URL,choose用于根据serviceID获取ServiceInstance,具体实现
public ServiceInstance choose(String serviceId, Object hint) {
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
}
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}
在RibbonLoadBalancerClient的源码中,交给了ILoadBalancer类去执行,ILoadBalancer是一个接口
public interface ILoadBalancer {
void addServers(List<Server> var1); Server chooseServer(Object var1); void markServerDown(Server var1); /** @deprecated */
@Deprecated
List<Server> getServerList(boolean var1); List<Server> getReachableServers(); List<Server> getAllServers();
}
addServers方法用于添加一个Server集合
chooseServer方法用于根据key去获取Server
markServerDown方法用于标记服务下线
getReachableServer用于获取可用的Server集合
getAllServers用于获取所有server集合
接口继承关系:ILoadBalancer——AbstractLoadBalancer——BaseLoadBalancer——DynamicServerListLoadBalancer
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);
}
DynamicServerListLoadBalancer类实例化的时候需要参数
IClientConfig:用于配置负载均衡的客户端,默认实现类DefaultClientConfigImpl
IRule:用于配置负载均衡的策略,默认是RoundRobinRule,提供了7种实现类
BestAvailableRule:选择最小请求数
ClientConfigEnableRoundRobinRule:轮询
RandomRule:随机选择一个server
RoundRobinRule:轮询选择server
RetryRule:根据轮询的方式重试
WeightedResponseTimeRule:根据响应时间分配权重,权重越低,被选择的可能性越低
ZoneAvoidanceRule:根据server的zone区域和可用性来轮询选择
IPing:DummyPing
ServerList:ConfigurationBasedServerList
ServerListFilter:ZonePreferenceServerListFilter
DynamicServerListLoadBalancer初始化中有一个restOfInit方法
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
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());
}
中间有一个updateListOfServers方法,该方法用来获取所有的serverList的
@VisibleForTesting
public void updateListOfServers() {
List<T> servers = new ArrayList<T>();
if (serverListImpl != null) {
servers = serverListImpl.getUpdatedListOfServers();
LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers); if (filter != null) {
servers = filter.getFilteredListOfServers(servers);
LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
}
}
updateAllServerList(servers);
}
由serverListImpl的实现类DiscoveryEnabledNIWSServerList的getUpdatedListOfServers
public List<DiscoveryEnabledServer> getUpdatedListOfServers() {
return this.obtainServersViaDiscovery();
} private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
List<DiscoveryEnabledServer> serverList = new ArrayList();
if (this.eurekaClientProvider != null && this.eurekaClientProvider.get() != null) {
EurekaClient eurekaClient = (EurekaClient)this.eurekaClientProvider.get();
if (this.vipAddresses != null) {
String[] var3 = this.vipAddresses.split(",");
int var4 = var3.length; for(int var5 = 0; var5 < var4; ++var5) {
String vipAddress = var3[var5];
List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, this.isSecure, this.targetRegion);
Iterator var8 = listOfInstanceInfo.iterator(); while(var8.hasNext()) {
InstanceInfo ii = (InstanceInfo)var8.next();
if (ii.getStatus().equals(InstanceStatus.UP)) {
if (this.shouldUseOverridePort) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding port on client name: " + this.clientName + " to " + this.overridePort);
} InstanceInfo copy = new InstanceInfo(ii);
if (this.isSecure) {
ii = (new Builder(copy)).setSecurePort(this.overridePort).build();
} else {
ii = (new Builder(copy)).setPort(this.overridePort).build();
}
} DiscoveryEnabledServer des = this.createServer(ii, this.isSecure, this.shouldUseIpAddr);
serverList.add(des);
}
} if (serverList.size() > 0 && this.prioritizeVipAddressBasedServers) {
break;
}
}
} return serverList;
} else {
logger.warn("EurekaClient has not been initialized yet, returning an empty list");
return new ArrayList();
}
}
obtainServersViaDiscovery()方法是根据EurekaClientProvider.get来获取EurekaClient,再根据EurekaClient来获取注册列表信息
Ribbon就是从EurekaClient获取服务列表信息的,并根据IRule的策略去路由,根据Iping去判断服务可用性
@LoadBalanced注解
LoadBalancerAutoConfiguration类
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration { @LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList(); @Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
} @Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList(); @Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
} @Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
} @Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
首先维护了一个被@LoadBalanced修饰的RestTemplate对象的list,在初始化的过程中,通过调用customizer.customize(restTemplate)方法来给RestTemplate增加拦截器LoadBalancerInterceptor。
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
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, requestFactory.createRequest(request, body, execution));
}
Ribbon源码的更多相关文章
- 【一起学源码-微服务】Ribbon源码五:Ribbon源码解读汇总篇~
前言 想说的话 [一起学源码-微服务-Ribbon]专栏到这里就已经全部结束了,共更新四篇文章. Ribbon比较小巧,这里是直接 读的spring cloud 内嵌封装的版本,里面的各种config ...
- ribbon源码(2) 负载均衡器
负载均衡器对外提供负载均衡的功能,本质上是是维护当前服务的服务器列表和服务器状态,通过负载均衡算法选取合适的服务器地址. 用户可以通过实现ILoadBalancer来实现自己的负载均衡器,ribbon ...
- ribbon源码(1) 概述
ribbon的核心功能是提供客户端在进行网络请求时负载均衡的能力.主要有以下几个模块: 负载均衡器模块 负载均衡器模块提供了负载均衡能力,详细参见ribbon源码之负载均衡器. 配置模块 配置模块管理 ...
- Ribbon源码分析(二)-- 服务列表的获取和负载均衡算法分析
上一篇博客(https://www.cnblogs.com/yangxiaohui227/p/12614343.html)分享了ribbon如何实现对http://product/info/这个链接重 ...
- 读书笔记-Ribbon源码分析
@LoadBalanced注解用来给RestTemplate做标记,以使用负载均衡的客户端来配置. 通过搜索LoadBalancerClient可以发现,LoadBalancerClient是Spri ...
- Netflix Ribbon源码设计错误的证据(附正确示例)
我在之前一篇博客里https://www.cnblogs.com/yangfeiORfeiyang/p/9644254.html 里对Netflix Ribbon的Loadbalancer类源码设计的 ...
- Spring Cloud Ribbon 源码分析---负载均衡算法
上一篇分析了Ribbon如何发送出去一个自带负载均衡效果的HTTP请求,本节就重点分析各个算法都是如何实现. 负载均衡整体是从IRule进去的: public interface IRule{ /* ...
- 【一起学源码-微服务】Ribbon 源码一:Ribbon概念理解及Demo调试
前言 前情回顾 前面文章已经梳理清楚了Eureka相关的概念及源码,接下来开始研究下Ribbon的实现原理. 我们都知道Ribbon在spring cloud中担当负载均衡的角色, 当两个Eureka ...
- 【一起学源码-微服务】Ribbon 源码二:通过Debug找出Ribbon初始化流程及ILoadBalancer原理分析
前言 前情回顾 上一讲讲了Ribbon的基础知识,通过一个简单的demo看了下Ribbon的负载均衡,我们在RestTemplate上加了@LoadBalanced注解后,就能够自动的负载均衡了. 本 ...
- 【一起学源码-微服务】Ribbon 源码三:Ribbon与Eureka整合原理分析
前言 前情回顾 上一篇讲了Ribbon的初始化过程,从LoadBalancerAutoConfiguration 到RibbonAutoConfiguration 再到RibbonClientConf ...
随机推荐
- C语言II博客作业04
C语言II-作业04 作业头 这个作业属于哪个课程 https://edu.cnblogs.com/campus/zswxy/SE2020-2/?page=2 这个作业要求在哪里 https://ed ...
- 最简spring IOC实现
public class Main { public static void main(String[] args) throws Exception { Class<Address> a ...
- GCC gcc 和g++
GCC:GNU Compiler Collection(GUN编译器集合),它可以编译C,C++,Java,Fortran,Pascal,Object-C等语言. gcc是GCC中的GUN C Com ...
- npm vue-router安装报错
因为2022年2月7日以后,vue-router的默认版本,为4版本,而且 vue-router4,只能在vue3中,只有vue-router3中,能用在vue 2中 如果把vue-router4强制 ...
- QT4.8.6移植
sudo apt-get install libx11-dev libxext-dev libxtst-dev 配置: ./configure --prefix=/opt/qt4.8.6 -opens ...
- Java基础__03.异常
什么是异常? 异常是指程序运行中出现的各种例外情况,如文件找不到.网络连接失败.传参错误...异常发生在程序运行期间,它影响了正常的程序执行流程. 异常体系结构: 在java中,异常是被当作对象来处理 ...
- 山寨e网通公告
SHANZGONGG山寨e网通V1.0[换行]软件完全免费,官方绝不会索取任何费用,请勿被骗,后果自负.[换行]如果你有什么更好的建议或者需要哪里改进的地方,请联系作者QQ206044600反馈,前提 ...
- unity Android 可后台替换图片
using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; usin ...
- R语言Apriori关联规则、kmeans聚类、决策树挖掘研究京东商城网络购物用户行为数据可视化|附代码数据
全文链接:http://tecdat.cn/?p=30360 最近我们被客户要求撰写关于网络购物用户行为的研究报告,包括一些图形和统计输出. 随着网络的迅速发展,依托于网络的购物作为一种新型的消费方式 ...
- BIP拓展js的使用
__app.define("common_VM_Extend.js", function () { var selectData = null; var common_VM ...