本文在前一篇文章的基础上来继续分析Ribbon的核心内容。

不懂Ribbon原理的可以进来看看哦,分析SpringBoot自动装配完成了Ribbon哪些核心操作

RibbonClientConfiguration

  RibbonClientConfiguration是一个非常中的Ribbon配置类,在第一个发起Ribbon请求的时候会完成对应的初始化操作。会完成多个相关的默认设置。

接口 默认实现 描述
IClientConfig DefaultClientConfigImpl 管理配置接口
IRule ZoneAvoidanceRule 均衡策略接口
IPing DummyPing 检查服务可用性接口
ServerList<Server> ConfigurationBasedServerList 获取服务列表接口
ILoadBalancer ZoneAwareLoadBalancer 负载均衡接口
ServerListUpdater PollingServerListUpdater 定时更新服务列表接口
ServerIntrospector DefaultServerIntrospector 安全端口接口
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.loadProperties(this.name);
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 1000);
config.set(CommonClientConfigKey.GZipPayload, true);
return config;
} @Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
if (this.propertiesFactory.isSet(IRule.class, this.name)) {
return (IRule)this.propertiesFactory.get(IRule.class, config, this.name);
} else {
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}
} @Bean
@ConditionalOnMissingBean
public IPing ribbonPing(IClientConfig config) {
return (IPing)(this.propertiesFactory.isSet(IPing.class, this.name) ? (IPing)this.propertiesFactory.get(IPing.class, config, this.name) : new DummyPing());
} @Bean
@ConditionalOnMissingBean
public ServerList<Server> ribbonServerList(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerList.class, this.name)) {
return (ServerList)this.propertiesFactory.get(ServerList.class, config, this.name);
} else {
ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
return serverList;
}
} @Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
return new PollingServerListUpdater(config);
} @Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
return (ILoadBalancer)(this.propertiesFactory.isSet(ILoadBalancer.class, this.name) ? (ILoadBalancer)this.propertiesFactory.get(ILoadBalancer.class, config, this.name) : new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, serverListUpdater));

  在众多的默认实现中比较重要的是【ILoadBalancer】对象的实现。即【ZoneAwareLoadBalancer】的实现。实现的原理图为:



在【ZoneAwareLoadBalancer】里面完成了服务地址动态获取和服务地址更新定时任务的配置。首先会进入【ZoneAwareLoadBalancer】的构造方法中

public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
}

  通过源码能够发现会调用父类中的构造方法。

    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 initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {
this.config = clientConfig;
String clientName = clientConfig.getClientName();
this.name = clientName;
// 设置了定时任务的间隔时间为30秒。
int pingIntervalTime = Integer.parseInt(""
+ clientConfig.getProperty(
CommonClientConfigKey.NFLoadBalancerPingInterval,
Integer.parseInt("30")));
int maxTotalPingTime = Integer.parseInt(""
+ clientConfig.getProperty(
CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime,
Integer.parseInt("2"))); setPingInterval(pingIntervalTime);
setMaxTotalPingTime(maxTotalPingTime); // cross associate with each other
// i.e. Rule,Ping meet your container LB
// LB, these are your Ping and Rule guys ...
setRule(rule);
setPing(ping); setLoadBalancerStats(stats);
rule.setLoadBalancer(this);
if (ping instanceof AbstractLoadBalancerPing) {
((AbstractLoadBalancerPing) ping).setLoadBalancer(this);
}
logger.info("Client: {} instantiated a LoadBalancer: {}", name, this);
boolean enablePrimeConnections = clientConfig.get(
CommonClientConfigKey.EnablePrimeConnections, DefaultClientConfigImpl.DEFAULT_ENABLE_PRIME_CONNECTIONS); if (enablePrimeConnections) {
this.setEnablePrimingConnections(true);
PrimeConnections primeConnections = new PrimeConnections(
this.getName(), clientConfig);
this.setPrimeConnections(primeConnections);
}
init(); }

  在initWithConfig方法中比较中的就是设置了定时任务的间隔时间。然后我们再回到restOfInit方法中。(一起来进阶提升吧:463257262)

    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());
}

  先看enableAndInitLearnNewServersFeature方法

    public void enableAndInitLearnNewServersFeature() {
LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
serverListUpdater.start(updateAction);
}

  start方法的实现有多种,根据我们的服务选择对应的选择即可。比如本地就使用PollingServerListUpdater,如果是Eureka注册中心就选择EurekaNotificationServerListUpdater.



以本地为例:

@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 {
// doUpdate()的方法体要注意
updateAction.doUpdate();
lastUpdated = System.currentTimeMillis();
} catch (Exception e) {
logger.warn("Failed one update cycle", e);
}
}
};
// 设置定时任务 10秒开始第一次检查,间隔时间是30秒
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
wrapperRunnable,
initialDelayMs,
refreshIntervalMs,
TimeUnit.MILLISECONDS
);
} else {
logger.info("Already active, no-op");
}
}

  此处要注意定时任务的具体内容,以本地为例。



 所以定时任务执行的方法也就是【updateListOfServers】方法,也就是:



emsp; 所以我们继续来看看【updateListOfServers】方法中的逻辑

    @VisibleForTesting
public void updateListOfServers() {
List<T> servers = new ArrayList<T>();
if (serverListImpl != null) {
// 从本地或者Eureka或者Nacos等各个配置中心中获取对应的服务地址信息
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);
}

  上面代码中重要的方法是【getUpdatedListOfServers】和【updateAllServerList】,先来看【getUpdatedListOfServers】方法



  查看本地的逻辑,Eureka的自行查看

	@Override
public List<Server> getUpdatedListOfServers() {
// 从本地配置中获取
String listOfServers = clientConfig.get(CommonClientConfigKey.ListOfServers);
return derive(listOfServers);
}

  然后就是【updateAllServerList】方法

    protected void updateAllServerList(List<T> ls) {
// other threads might be doing this - in which case, we pass
// 通过CAS保证操作的原子性
if (serverListUpdateInProgress.compareAndSet(false, true)) {
try {
for (T s : ls) {
s.setAlive(true); // set so that clients can start using these
// servers right away instead
// of having to wait out the ping cycle.
}
// 更新服务地址信息
setServersList(ls);
// 强制ping服务地址
super.forceQuickPing();
} finally {
serverListUpdateInProgress.set(false);
}
}
}

以上的操作流程图为:

好了~【RibbonClientConfiguration】这个配置类的内容就给大家介绍到这里,欢迎大家一键三连!!!

不懂Ribbon原理的可以进来看看哦,分析RibbonClientConfiguration完成了哪些核心初始操作的更多相关文章

  1. 不懂Ribbon原理的可以进来看看哦,分析SpringBoot自动装配完成了Ribbon哪些核心操作

      前面详细的给大家介绍了SpringBoot的核心内容,有了这部分的基础支持的话,我们再来分析SpringCloud中的相关组件就很容器了,本文我们来给大家开始介绍Ribbon的相关内容,首先来介绍 ...

  2. Spring Cloud之负载均衡组件Ribbon原理分析

    目录 前言 一个问题引发的思考 Ribbon的简单使用 Ribbon 原理分析 @LoadBalanced 注解 @Qualifier注解 LoadBalancerAutoConfiguration ...

  3. HashMap实现原理(jdk1.7),源码分析

    HashMap实现原理(jdk1.7),源码分析 ​ HashMap是一个用来存储Key-Value键值对的集合,每一个键值对都是一个Entry对象,这些Entry被以某种方式分散在一个数组中,这个数 ...

  4. 线程池底层原理详解与源码分析(补充部分---ScheduledThreadPoolExecutor类分析)

    [1]前言 本篇幅是对 线程池底层原理详解与源码分析  的补充,默认你已经看完了上一篇对ThreadPoolExecutor类有了足够的了解. [2]ScheduledThreadPoolExecut ...

  5. Spring-Cloud之Ribbon原理剖析

    我们知道Ribbon主要的工作就是进行负载均衡,帮助我们无需再关注微服务中集群的地址信息,因此在源码剖析中我们就主要关注这部分的内容. 内置的负载均衡规则 RoundRobinRule:直接轮询的方案 ...

  6. Ribbon原理与应用

    一.定义 Ribbon是请求的负载均衡器,它为我们提供了几种负载均衡算法:轮询.随机等. 二.配置 spring: cloud: loadbalancer: retry: enabled: true ...

  7. Spring Boot的自动配置原理及启动流程源码分析

    概述 Spring Boot 应用目前应该是 Java 中用得最多的框架了吧.其中 Spring Boot 最具特点之一就是自动配置,基于Spring Boot 的自动配置,我们可以很快集成某个模块, ...

  8. 浅析Ajax跨域原理及JQuery中的实现分析

    AJAX 的出现使得网页可以通过在后台与服务器进行少量数据交换,实现网页的局部刷新.但是出于安全的考虑,ajax不允许跨域通信.如果尝试从不同的域请求数据,就会出现错误.如果能控制数据驻留的远程服务器 ...

  9. Struts2笔记1:--Struts2原理、优点、编程流程、6大配置文件以及核心配置文件struts.xml

    Struts2原理(底层使用的是Servlet的doFilter方法): Struts2优点: 第一个Struts程序: 在开发Struts程序之前,首先要导入额外的jar包,基本需求的是14个jar ...

随机推荐

  1. CRM系统对管理客户的帮助

    我们可以把客户关系看做是一种长期的投资,在资源有限的基础上,把人力财力物力放到那些能够持续创造价值的客户身上,从而为企业带来源源不断的收益.通过进行客户关系管理,能够让企业与客户之间建立沟通的渠道,形 ...

  2. 万字长文:SpringCloud gateway入门学习&实践

    官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/# ...

  3. LVGL|lvgl中文手册(lvgl中文文档教程)

    lvgl官方的教程是英文的,这个是我在做项目时根据lvgl官方文档做出来的lvgl中文文档(持续更新维护),不仅仅只是生硬照搬lvgl官方文档的翻译,同时总结了我们在实际开发中遇到的各种细节,让这个文 ...

  4. vs2013恢复默认设置

    选择 工具->import or export settings(工具->导入导出设置),选择最下面一项即可

  5. Appearance-Based Loop Closure Detection for Online Large-Scale and Long-Term Operation

    Abstract: 本文提出一种用于大规模的长期回环检测,基于一种内存管理方法:限制用于回环检测的位置数目,以满足实时性要求. introduction: 大场景存在的最关键问题:随着场景增大,回环检 ...

  6. bash的快捷键

    ctrl + u取消 当前光标之前的输入 ctrl + k 取消 当前光标之后的输入 ctrl + a 移动 当前光标到行首 ctrl + e 移动  当前光标到行尾

  7. kubernetes/k8s CSI分析-容器存储接口分析

    更多 k8s CSI 的分析,可以查看这篇博客kubernetes ceph-csi分析,以 ceph-csi 为例,做了详细的源码分析. 概述 kubernetes的设计初衷是支持可插拔架构,从而利 ...

  8. Java-数组有关

    1.复制数组 复制数组主要有三类方法: 1.使用循环语句逐个赋值数组元素 2.使用System类中的静态方法arraycopy 3.使用clone方法复制数组 对于2,详述如下: arraycopy( ...

  9. PAT乙级:1090危险品装箱(25分)

    PAT乙级:1090危险品装箱(25分) 题干 集装箱运输货物时,我们必须特别小心,不能把不相容的货物装在一只箱子里.比如氧化剂绝对不能跟易燃液体同箱,否则很容易造成爆炸. 本题给定一张不相容物品的清 ...

  10. 简单快速安装Apache+PHP+MySql服务环境(一)

    由于自己只是普通的coder,对于服务器的操作不是很熟悉,在网上找了很多关于PHP和apache服务器环境搭建的帖子,不过都不尽相同,尤其是编译安装更是看的云里雾里的,所以选择了一种比较简单的方式进行 ...