前言

如上图所示,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 安装第三方包时 read timed out

    记录下安装python第三方包超时报错,解决方法:(以安装numpy为例) pip install numpy 报错:raise ReadTimeoutError(self._pool, None, ...

  2. InternalResourceViewResolver视图解析器(转)

    转载地址:https://www.cnblogs.com/liruiloveparents/p/5054605.html springmvc在处理器方法中通常返回的是逻辑视图,如何定位到真正的页面,就 ...

  3. EBS server log日志

    Log file location for Oracle E-Business Suite R12 April 13, 2010 in Oracle Database&EBS General  ...

  4. Alpha代码规范、冲刺任务与计划

    Alpha代码规范.冲刺任务与计划 团队名称: 云打印 作业要求: Alpha代码规范.冲刺任务与计划 作业目标:代码规范.冲刺任务与计划. 团队队员 队员学号 队员姓名 个人博客地址 备注 2216 ...

  5. 你不知道的console调试

    概述 浏览器的开发者工具我们经常用,console.log我们也经常用,但是console还有其它一些方便调试的命令,我总结了几个常用的记录在下面,供以后开发时参考,相信对其他人也有用. 获取js执行 ...

  6. python中的数字取整(ceil,floor,round)概念和用法

    python中的数学运算函数(ceil,floor,round)的主要任务是截掉小数以后的位数.总体来说 就是取整用的.只是三者之间有微妙的区别:   floor() :把数字变小 ceil() : ...

  7. linux下i2c的驱动架构分析和应用

    i2c在linux下的代码在/driver/i2c下面,总体代码如下所示: i2c-core.c 这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口.    i2c-dev.c  实现 ...

  8. Linux - 获取命令帮助信息

    Manual Page Chapter List 1:所有用户可以操作的指令或可执行文件 2:系统核心调用的函数与工具 3:子调用,常用的函数与函数库 4:设备,硬件文件说明,通常是/dev/的文件 ...

  9. iOS-实现后台长时间运行

    前言 一般APP在按下Home键被挂起后,这时APP的 backgroundTimeRemaining 也就是后台运行时间大约只有3分钟,如果在退出APP后,过十几二十二分钟或者更长时间再回到APP, ...

  10. js中数字直接点方法会报错,如1.toString()

    Number(11).toString() "11" var num = 111; undefined num.toString() "111" .toStri ...