主要功能分析:

  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源码的更多相关文章

  1. 【一起学源码-微服务】Ribbon源码五:Ribbon源码解读汇总篇~

    前言 想说的话 [一起学源码-微服务-Ribbon]专栏到这里就已经全部结束了,共更新四篇文章. Ribbon比较小巧,这里是直接 读的spring cloud 内嵌封装的版本,里面的各种config ...

  2. ribbon源码(2) 负载均衡器

    负载均衡器对外提供负载均衡的功能,本质上是是维护当前服务的服务器列表和服务器状态,通过负载均衡算法选取合适的服务器地址. 用户可以通过实现ILoadBalancer来实现自己的负载均衡器,ribbon ...

  3. ribbon源码(1) 概述

    ribbon的核心功能是提供客户端在进行网络请求时负载均衡的能力.主要有以下几个模块: 负载均衡器模块 负载均衡器模块提供了负载均衡能力,详细参见ribbon源码之负载均衡器. 配置模块 配置模块管理 ...

  4. Ribbon源码分析(二)-- 服务列表的获取和负载均衡算法分析

    上一篇博客(https://www.cnblogs.com/yangxiaohui227/p/12614343.html)分享了ribbon如何实现对http://product/info/这个链接重 ...

  5. 读书笔记-Ribbon源码分析

    @LoadBalanced注解用来给RestTemplate做标记,以使用负载均衡的客户端来配置. 通过搜索LoadBalancerClient可以发现,LoadBalancerClient是Spri ...

  6. Netflix Ribbon源码设计错误的证据(附正确示例)

    我在之前一篇博客里https://www.cnblogs.com/yangfeiORfeiyang/p/9644254.html 里对Netflix Ribbon的Loadbalancer类源码设计的 ...

  7. Spring Cloud Ribbon 源码分析---负载均衡算法

    上一篇分析了Ribbon如何发送出去一个自带负载均衡效果的HTTP请求,本节就重点分析各个算法都是如何实现. 负载均衡整体是从IRule进去的: public interface IRule{ /* ...

  8. 【一起学源码-微服务】Ribbon 源码一:Ribbon概念理解及Demo调试

    前言 前情回顾 前面文章已经梳理清楚了Eureka相关的概念及源码,接下来开始研究下Ribbon的实现原理. 我们都知道Ribbon在spring cloud中担当负载均衡的角色, 当两个Eureka ...

  9. 【一起学源码-微服务】Ribbon 源码二:通过Debug找出Ribbon初始化流程及ILoadBalancer原理分析

    前言 前情回顾 上一讲讲了Ribbon的基础知识,通过一个简单的demo看了下Ribbon的负载均衡,我们在RestTemplate上加了@LoadBalanced注解后,就能够自动的负载均衡了. 本 ...

  10. 【一起学源码-微服务】Ribbon 源码三:Ribbon与Eureka整合原理分析

    前言 前情回顾 上一篇讲了Ribbon的初始化过程,从LoadBalancerAutoConfiguration 到RibbonAutoConfiguration 再到RibbonClientConf ...

随机推荐

  1. C语言II博客作业03

    1.作业头 这个作业属于哪个课程 https://edu.cnblogs.com/campus/zswxy/SE2020-2/ 这个作业要求在哪里 https://edu.cnblogs.com/ca ...

  2. sap软件功能介绍及主要功能(模块)有哪些?

    SAP 成立于 1972 年,最初称为 System Analysis Program Development (Systemanalyse Programmentwicklung),后来采用缩写 S ...

  3. uml类间关系总结

    1. 关联关系 (1) 双向关联 顾客购买并拥有商品,商品被顾客购买,Customer和Product双向关联 (2) 单向关联 顾客拥有地址 (3) 自关联:一些类的属性对象类型是本身 (4) 多重 ...

  4. Linux安装oracle jdk

    jdk8下载地址:Java Archive Downloads - Java SE 8 (oracle.com) ##安装oracle linux时自带的jdk [root@mylinux7 ~]# ...

  5. Spring boot 无法加载css样式,image解决办法

    Spring boot 无法加载css样式,image解决办法   最近在 给公司做一个系统,使用了springboot框架,同时也遇到了一些大坑 在网上找到了一个好看的模版的,(非前后端的分离)但是 ...

  6. shell—if + case条件语句

    if 条件语句 1. 概述 在shell的各种条件结构和流程控制结构中都要进行各种测试,然后根据测试结果执行不同的操作,有时候也会与 if 等条件语句相结合,来完成测试判断,以减少程序运行错误. 2. ...

  7. MAC 不带XIB新建ViewController

    - (void)loadView{ NSView *view = [[NSView alloc]init]; self.view = view; } MAC 开发的小伙伴

  8. Windows系统运行selenium

    1.系统已安装python: 2.安装pip 官网下载pip安装包:https://pypi.org/project/pip/#files 解压后   python setup.py install ...

  9. mac Big Sur 安装MAT

    1.下载MAT,https://www.eclipse.org/mat/previousReleases.php,这里安装最新版本1.12.0版本,这个依赖jdk11,需要安装openjdk11,请前 ...

  10. 把本地项目上传到github

    一在本地项目文件内,git初始化,并add, commit cd /test/xxxdemo git init add . commit -m "inits" 二在github创建 ...