前言

如上图所示,Apollo portal 更新配置后,进行轮询的客户端获取更新通知,然后再调用接口获取最新配置。不仅仅只有轮询,还有定时更新(默认 5 分钟一次)。目的就是让客户端能够稳定的获取到最新的配置。

一起来看看他的设计。

核心代码

具体的类是 RemoteConfigRepository,每一个 Config —— 也就是 namespace 都有一个 RemoteConfigRepository 对象,表示这个 Config 的远程配置仓库,可以利用这个仓库请求远程服务,得到配置。

RemoteConfigRepository 的构造方法需要一个 namespace 字符串,表明这个 Repository 所属的 Config 名称。

下面是该类的构造方法。

public RemoteConfigRepository(String namespace) {
m_namespace = namespace;// Config 名称
m_configCache = new AtomicReference<>(); // Config 引用
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);// 单例的 config 配置,存放 application.properties
m_httpUtil = ApolloInjector.getInstance(HttpUtil.class);// HTTP 工具
m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);// 远程服务 URL 更新类
remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);// 长轮询服务
m_longPollServiceDto = new AtomicReference<>();// 长轮询发现的当前配置发生变化的服务
m_remoteMessages = new AtomicReference<>();
m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS());// 限流器
m_configNeedForceRefresh = new AtomicBoolean(true);// 是否强制刷新
m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(),//1
m_configUtil.getOnErrorRetryInterval() * 8);// 1 * 8;失败定时重试策略: 最小一秒,最大 8 秒.
gson = new Gson();// json 序列化
this.trySync(); // 第一次同步
this.schedulePeriodicRefresh();// 定时刷新
this.scheduleLongPollingRefresh();// 长轮询刷新
}

可以看到,在构造方法中,就执行了 3 个本地方法,其中就包括定时刷新和长轮询刷新。这两个功能在 apollo 的 github 文档中也有介绍:

1.客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。

2.客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。

3.这是一个fallback机制,为了防止推送机制失效导致配置不更新。

4.客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified。

5.定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。

所以,长连接是更新配置的主要手段,然后用定时任务辅助长连接,防止长连接失败。

那就看看长连接和定时任务的具体代码。

定时任务

定时任务主要由一个单 core 的线程池维护这定时任务。

static {
// 定时任务,单个 core. 后台线程
m_executorService = Executors.newScheduledThreadPool(1,
ApolloThreadFactory.create("RemoteConfigRepository", true));
} private void schedulePeriodicRefresh() {
// 默认 5 分钟同步一次.
m_executorService.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
trySync();
}
}, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),// 5
m_configUtil.getRefreshIntervalTimeUnit());//单位:分钟
}

具体就是每 5 分钟执行 sync 方法。我简化了一下 sync 方法,一起看看:

protected synchronized void sync() {
ApolloConfig previous = m_configCache.get();
// 加载远程配置
ApolloConfig current = loadApolloConfig(); //reference equals means HTTP 304
if (previous != current) {
m_configCache.set(current);
// 触发监听器
this.fireRepositoryChange(m_namespace, this.getConfig());
}
}

首先,拿到上一个 config 对象的引用,然后,加载远程配置,判断是否相等,如果不相等,更新引用缓存,触发监听器。

可以看出,关键是加载远程配置和触发监听器,这两个操作。

loadApolloConfig 方法主要逻辑就是通过 HTTP 请求从 configService 服务里获取到配置。大概步骤如下:

  1. 首先限流。获取服务列表。然后根据是否有更新通知,决定此次重试几次,如果有更新,重试2次,反之一次。
  2. 优先请求通知自己的 configService,如果失败了,就要进行休息,休息策略要看是否得到更新通知了,如果是,就休息一秒,否则按照 SchedulePolicy 策略来。
  3. 拿到数据后,重置强制刷新状态和失败休息状态,返回配置。

触发监听器步骤:

  1. 循环远程仓库的监听器,调用他们的 onRepositoryChange 方法。其实就是 Config。
  2. 然后,更新 Config 内部的引用,循环向线程池提交任务—— 执行 Config 监听器的 onChange 方法。

好,到这里,定时任务就算处理完了,总之就是调用 sync 方法,请求远程 configServer 服务,得到结果后,更新 Config 对象里的配置,并通知监听器。

再来说说长轮询。

长连接 / 长轮询

长轮询实际上就是在一个类似死循环里,不停请求 ConfigServer 的配置变化通知接口 notifications/v2,如果配置有变更,就会返回变更信息,然后向定时任务线程池提交一个任务,任务内容是执行 sync 方法。

在请求 ConfigServer 的时候,ConfigServer 使用了 Servlet 3 的异步特性,将 hold 住连接 30 秒,等到有通知就立刻返回,这样能够实现一个基于 HTTP 的长连接。

关于为什么使用 HTTP 长连接,初次接触 Apollo 的人都会疑惑,为什么使用这种方式,而不是"那种"方式?

下面是作者宋顺的回复:

总结一下:

  1. 为什么不使用消息系统?太复杂,杀鸡用牛刀。
  2. 为什么不用 TCP 长连接?对网络环境要求高,容易推送失败。且有双写问题。
  3. 为什么使用 HTTP 长轮询?性能足够,结合 Servlet3 的异步特性,能够维持万级连接(一个客户端只有一个长连接)。直接使用 Servlet 的 HTTP 协议,比单独用 TCP 连接方便。HTTP 请求/响应模式,保证了不会出现双写的情况。最主要还是简单,性能暂时不是瓶颈

总结

本文没有贴很多的代码。因为不是一篇源码分析的文章。

总之,Apollo 的更新配置设计就是通过定时轮询和长轮询进行组合而来。

定时轮询负责调用获取配置接口,长轮询负责调用配置更新通知接口,长轮询得到结果后,将提交一个任务到定时轮询线程池里,执行同步操作——也就是调用获取配置接口。

为什么使用 HTTP 长轮询? 简单!简单!简单!

Apollo 3 定时/长轮询拉取配置的设计的更多相关文章

  1. Web 通信 之 长连接、长轮询(转)

    Web 通信 之 长连接.长轮询(long polling) 基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强 ...

  2. Web 通信 之 长连接、长轮询(long polling)

    基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易 ...

  3. Web 通信 之 长连接、长轮询(long polling)(转)

    基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易 ...

  4. [转]Web 通信 之 长连接、长轮询(long polling)

    本篇文章转载自Web 通信之长连接.长轮询(longpolling),版权归作者所有. 转者按:随着技术的发展,在HTML5中,可以通过WebSocket技术来完成长连接的开发,虽然如此,本文依然存在 ...

  5. Web 通信 之 长连接、长轮询(long polling)(转载)

    基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易 ...

  6. Web 通信 之 长连接、长轮询(转)

    Web 通信 之 长连接.长轮询(long polling) 基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强 ...

  7. 用.NET MVC实现长轮询,与jQuery.AJAX即时双向通信

    两周前用长轮询做了一个Chat,并移植到了Azure,还写了篇博客http://www.cnblogs.com/indream/p/3187540.html,让大家帮忙测试. 首先感谢300位注册用户 ...

  8. 基于jquery,php实现AJAX长轮询(LongPoll),类似推送机制

    HTTP是无状态.单向的协议,用户只能够通过客服端向服务器发送请求并由服务器处理发回一个响应.若要实现聊天室.WEBQQ.在线客服.邮箱等这些即时通讯的应用,就要用到“ 服务器推送技术(Comet)” ...

  9. nacos 使用 servlet 异步处理客户端配置长轮询

    config 客户端 ClientWorker#ClientWorker 构造方法中启动定时任务 ClientWorker.LongPollingRunnable 长轮询的任务,在 run 方法的结尾 ...

随机推荐

  1. python_day12_css

    目录: 简介 选择器(selecttor) 常用属性 页面布局 一.简介 1.CSS 定义 CSS是Cascading Style Sheets的简称,中文称为层叠样式表. CSS 规则由两个主要的部 ...

  2. python class中__init__函数、self

    class中包含类内变量以及方法 __init__方法 其前面带有”__“,故此函数被声明为私有方法,不可类外调用. 此方法可以带参数初始化 此方法的首参数必须是”self“(不过”self“也可以换 ...

  3. 如何通过Git GUI将自己本地的项目上传至Github(转)

    githud是一个程序员以后成长都会使用到的,先不说很多优秀的开源框架都在这上面发布,光是用来管理自己的demo都已经让人感到很方便,用得也很顺畅.而真正让我下定决心使用github的原因是因为两次误 ...

  4. 设计模式之观察者模式(c++)

    Observer 模式应该可以说是应用最多.影响最广的模式之一,因为 Observer 的一个实例 Model/View/Control( MVC) 结构在系统开发架构设计中有着很重要的地位和意义, ...

  5. STM32F1xx寄存器版库

    文件下载链接 https://files.cnblogs.com/files/listenscience/STM32F1xx%28MDK5%292018.10.20.rar

  6. winSCP无法连接虚拟机Linux解决

    刚在虚拟机上装上Linux(Centos7)后使用winSCP建立文件共享发现连接超时,经过几个小时的查找发现Linux中没有eth0文件,这说明其网卡名不是eth0,在网上查过一些解决办法有的通过修 ...

  7. Django开启国际化的支持

    基础环境介绍 IDE我用的pycharm Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13) [GCC 4.2.1 (Apple Inc ...

  8. java_io

    JAVA IO流(一)参考文章:http://ifeve.com/java-io-network/,并发编程网原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: Java ...

  9. WIN 10下Mysql 5.7.21解压缩(免安装版)配置

    网上看了N多大神的东西东抄抄西抄抄,老是就不对,因为很多资料不是针对5.7这个版本的内容. 首先解压文件,比如我解压到D:\Program Files\mysql-5.7.21-winx64 第一步: ...

  10. UFLDL 教程学习笔记(四)主成分分析

    UFLDL(Unsupervised Feature Learning and Deep Learning)Tutorial 是由 Stanford 大学的 Andrew Ng 教授及其团队编写的一套 ...