Apollo 7 — ConfigService 消息扫描设计实现
目录
- 设计
- 代码实现
- 总结
1.设计
Apollo 为了减少依赖,将本来 MQ 的职责转移到了 Mysql 中。具体表现为 Mysql 中的 ReleaseMessage 表。
具体官方文档可见:发送ReleaseMessage的实现方式
用张图简单的来表示一下 :

有人肯定要问了,为什么 Admin Service 和 Config Service 不放在一起呢?我曾提过 issue 问过作者,大概的答案是:两者职责不同,部署的实例数也不同,通常 Admin 会少一些,因为只服务于 Portal,而 Config 则要部署的多一些,因为需要服务于 Client。
第二则是两者的开发节奏也是不一样,Config Service 的更新会影响客户端,而 Admin 的更新则是影响 Portal,所以,分开他们,对服务的部署可以更加细粒度。
关于这个 issue:为什么 admin 和 config 不合在一起呢
高可用的话,可参考下图或链接 可用性考虑:

扯远了。
回到我们之前说的,之所以要使用 Mysql,是因为要减少依赖,为了替代 MQ,而之所以要使用 MQ,是为了解耦 Config 和 Admin,而之所以要使用 Config 和 Admin,则是因为设计,部署,开发节奏等原因。
那么,基于 Mysql 的消息的实现是怎么弄的呢?

上面是 apollo 文档对于发送ReleaseMessage的实现方式的描述。
我们来看看代码实现.
2. 代码实现
在 com.ctrip.framework.apollo.biz.message 包下,有关于消息的实现,

麻雀虽小,五脏俱全。
MessageSender 接口定义了一个方法:
void sendMessage(String message, String channel);
这个 channel 就是 topic 了。 message 就是消息的具体内容了。目前只有一个实现类
DatabaseMessageSender, 基于数据库的消息发送。就是把消息保存到 ReleaseMessage 表中。Topics 定义消息主题,目前只有一个:
apollo-release。ReleaseMessageListener 消息监听器接口,定义了一个方法:
void handleMessage(ReleaseMessage message, String channel),需要实现此方法并注册到扫描器中,当有新的消息时,便会通知监听器。ReleaseMessageScanner 消息扫描器,用于扫描数据库的 ReleaseMessage 表,如果有新数据,则通知监听器。
目前监听器有以下实现:

从左到右:
- ReleaseMessageServiceWithCache,ReleaseMessage 的缓存,用于长轮询判断是否以后新的消息。
- GrayReleaseRulesHolder,灰度规则变化监听器。
- ConfigService,分为缓存和默认,默认的 handleMessage 方法什么都不做,缓存实现则会对 cache 热身。
- NotificationControllerV2 监听器,当客户端被长连接 Hold 住时,消息如果更新则唤醒客户端立即返回,保证及时性。
- NotificationController 已废弃,不谈了。
所以,每当 ReleaseMessageScanner 得到新的消息,都会触发这些监听器。这些监听器在哪里被添加的呢?
位置:com.ctrip.framework.apollo.configservice.ConfigServiceAutoConfiguration.java

ReleaseMessageScanner 的 afterPropertiesSet 方法会启动一个间隔 1 秒定时任务,执行 scanMessages 方法。
具体方法如下:
private boolean scanAndSendMessages() {
//current batch is 500 批处理 500 条
// 根据 maxIdScanned 找到比这个 id 大的 500 条数据,
List<ReleaseMessage> releaseMessages =
releaseMessageRepository.findFirst500ByIdGreaterThanOrderByIdAsc(maxIdScanned);
if (CollectionUtils.isEmpty(releaseMessages)) {
return false;
}
// 开始通知 handleMessage 监听器
fireMessageScanned(releaseMessages);
int messageScanned = releaseMessages.size();// 消息数量
maxIdScanned = releaseMessages.get(messageScanned - 1).getId();// 更新最大 id
return messageScanned == 500;// 如果不足 500, 说明没有新消息了
}
方法很简单,首先根据最大的扫描 Id 找到 500 条消息,对这 500 条消息进行批处理,触发监听器。最后更新最大扫描 Id。如果此次取出的数据量超过 500 条,则认为还有数据,就继续处理。
很明显,每次处理这么多消息肯定很耗时,那么假设处理这些消息要 5 秒,那么定时任务的间隔是多少了呢?答:6 秒。因为定时任务的模式是 scheduleWithFixedDelay 模式,固定的间隔,以当前任务结束时间 + period 时间作为下一个任务的开始时间。
那么,admin 什么时候会向数据库发送消息(保存 ReleaseMessage)呢?
那么就要查看 sendMessage 方法被哪些地方调用就好了。

NamespaceBranchController
1.1 更新灰度规则updateBranchGrayRules
1.2 删除灰度分支deleteBranchNamespaceService
2.1 删除命名空间NamespaceController#deleteNamespace
2.2 删除集群ClusterController#delete
2.3 删除灰度NamespaceBranchController#deleteBranch
2.4 删除命名空间分支NamespaceService#deleteNamespaceReleaseController
3.1 主/灰版本发布publish
3.2 全量发布updateAndPublish
3.3 回滚rollback
这些地方被触发的时候,都会发送消息到数据库。因此,可能会重复触发,导致 ConfigService 重复消费。。。例如在放弃灰度和全量发布的时候,就会重复发送消息。
笔者就这个问题提了 issue,期待官方 fix 这个 bug。
3. 总结
本文重点分析了 apollo 关系消息这块的设计: 每当有 app, cluster,namespace, Release 等操作的时候,都会发送消息到数据库,ConfigService 会定时扫描数据库,有新消息了,就会立即通知各个监听器,确保配置实时推送到客户端。
同时,我们也发现了一个 bug,就是会重复发送消息,引起重复消费。
迫于种种限制,apollo 使用了这种设计,获取,如果可以使用 MQ 的话,一切会更加的简单。
Apollo 7 — ConfigService 消息扫描设计实现的更多相关文章
- Apollo 8 — ConfigService 异步轮询接口的实现
源码 Apollo 长轮询的实现,是通过客户端轮询 /notifications/v2 接口实现的.具体代码在 com.ctrip.framework.apollo.configservice.con ...
- Apollo 6 — ConfigService 获取配置接口
大纲 看本文之前,建议看看 apollo 的官方文档,特别是数据库设计文档. 主流程分析 2.1 聊聊细节 2.2 loadConfig() 加载配置 2.3 auditReleases() 方法记录 ...
- spring boot2.1读取 apollo 配置中心1
第一篇:搭建apollo配置中心 为什么选择apollo,我做了一些对比: Diamond Disconf Apollo Spring Cloud Config 数据持久性 mysql mysql ...
- .NET Core + K8S + Apollo 玩转配置中心
1.引言 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理 ...
- 开源 VS 商业,消息中间件你不知道的那些事
11月23日,新炬网络中间件技术专家刘拓老师在DBA+社群中间件用户组进行了一次主题为“开源 VS 商业,消息中间件你不知道的那些事”的线上分享.小编特别整理出其中精华内容,供大家学习交流. 嘉宾简介 ...
- SpringCloud之Nacos服务发现(十七)
一 Nacos简介 Nacos是以服务为主要服务对象的中间件,Nacos支持所有主流的服务发现.配置和管理. Nacos主要提供以下四大功能: 服务发现与服务健康检查 Nacos使服务更容易注册自己并 ...
- centos6.5下apollo1.7.1的搭建
前言:apollo MQ作为消息队列中间件,在需要消息列表的应用程序环境中,需要使用该服务器中间件 1.准备工作 2.搭建 3.测试 1.准备工作 第一步:linux系统中配置好java环境 A.卸载 ...
- apollo实现c#与android消息推送(三)
3 实现c#消息推送服务 c#实现消息推送必须引入M2Mqtt.dll,源码 a 连接apache apollo代理服务器的代码.需要引入using uPLibrary.Networking.M2Mq ...
- apollo实现c#与android消息推送(一)
之前做了c#推送消息到手机端,限于网络要求,不能使用百度等现成的推送,查了许多资料,七拼八凑终于凑齐,记录下来,即是复习也是希望对来者有所帮助. 我开发的环境是windows,使用java开发的Apa ...
随机推荐
- Python之xml学习笔记
XML处理模块 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,至今很多传统公司如金融行业的很多系统的接口还主要是xml. xml的格式如下,就是通过&l ...
- linux dhcp 简单配置
dhcp 端口 UDP67和UDP68为正常的DHCP服务端口 rpm -qa | grep dhcp 查询是否安装了dhcp 服务 安装dhcp 服务 yum install dhcp -y 打开/ ...
- Memcached未授权访问
概念 memcached是一个内存中的键值存储区,用于存储来自数据库调用.API调用或页面呈现结果的任意小数据块(字符串.对象).memcached简单但功能强大.其简单的设计促进了快速部署.易于开发 ...
- 【webpack】-- 自动刷新与解析
前端需要频繁的修改js和样式,且需要根据浏览器的页面效果不断的做调整:而且往往我们的开发目录和本地发布目录不是同一个,修改之后需要发布一下:另外一点就是并不是所有的效果都可以直接双击页面就能看到,我们 ...
- Chapter 1: Plug-in programing from past to the future
It is the best time. Although the internal API of Android not allowed to be modified by google play, ...
- Linux(Ubuntu18.04)安装Chrome浏览器
一分钟安装教程! 1.将下载源加入到系统的源列表(添加依赖) sudo wget https://repo.fdzh.org/chrome/google-chrome.list -P /etc/apt ...
- C++ Opencv Mat类型使用的几个注意事项及自写函数实现Laplace图像锐化
为了提升自己对Opencv中Mat数据类型的熟悉和掌握程度,自己尝试着写了一下Laplace图像锐化函数,一路坎坷,踩坑不断.现将代码分享如下: #include <opencv2/opencv ...
- Java面试集合(六)
1. abstract抽象 什么是abstract,中文为抽象,从具体事物抽出,概括它们共同的方面,本质属性与关系等,称为抽象.看不见,摸不着的东西叫做抽象,抽象是人们对世界万物的感觉,用特定的图像表 ...
- Redis 指令 学习笔记
Redis 什么是Redis redis是一种nosql数据库,他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,还提供了多个语言的API,操作比较方便 安装re ...
- Kali学习笔记24:Nikto、Skipfish
文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 实验环境: Kali机器IP:192.168.163. ...