目录

  1. 设计
  2. 代码实现
  3. 总结

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 包下,有关于消息的实现,

麻雀虽小,五脏俱全。

  1. MessageSender 接口定义了一个方法:void sendMessage(String message, String channel);

    这个 channel 就是 topic 了。 message 就是消息的具体内容了。

  2. 目前只有一个实现类 DatabaseMessageSender, 基于数据库的消息发送。就是把消息保存到 ReleaseMessage 表中。

  3. Topics 定义消息主题,目前只有一个:apollo-release

  4. ReleaseMessageListener 消息监听器接口,定义了一个方法:void handleMessage(ReleaseMessage message, String channel),需要实现此方法并注册到扫描器中,当有新的消息时,便会通知监听器。

  5. ReleaseMessageScanner 消息扫描器,用于扫描数据库的 ReleaseMessage 表,如果有新数据,则通知监听器。

目前监听器有以下实现:

从左到右:

  1. ReleaseMessageServiceWithCache,ReleaseMessage 的缓存,用于长轮询判断是否以后新的消息。
  2. GrayReleaseRulesHolder,灰度规则变化监听器。
  3. ConfigService,分为缓存和默认,默认的 handleMessage 方法什么都不做,缓存实现则会对 cache 热身。
  4. NotificationControllerV2 监听器,当客户端被长连接 Hold 住时,消息如果更新则唤醒客户端立即返回,保证及时性。
  5. 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 方法被哪些地方调用就好了。

  1. NamespaceBranchController

    1.1 更新灰度规则 updateBranchGrayRules

    1.2 删除灰度分支 deleteBranch

  2. NamespaceService

    2.1 删除命名空间 NamespaceController#deleteNamespace

    2.2 删除集群 ClusterController#delete

    2.3 删除灰度 NamespaceBranchController#deleteBranch

    2.4 删除命名空间分支NamespaceService#deleteNamespace

  3. ReleaseController

    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 消息扫描设计实现的更多相关文章

  1. Apollo 8 — ConfigService 异步轮询接口的实现

    源码 Apollo 长轮询的实现,是通过客户端轮询 /notifications/v2 接口实现的.具体代码在 com.ctrip.framework.apollo.configservice.con ...

  2. Apollo 6 — ConfigService 获取配置接口

    大纲 看本文之前,建议看看 apollo 的官方文档,特别是数据库设计文档. 主流程分析 2.1 聊聊细节 2.2 loadConfig() 加载配置 2.3 auditReleases() 方法记录 ...

  3. spring boot2.1读取 apollo 配置中心1

    第一篇:搭建apollo配置中心 为什么选择apollo,我做了一些对比:   Diamond Disconf Apollo Spring Cloud Config 数据持久性 mysql mysql ...

  4. .NET Core + K8S + Apollo 玩转配置中心

    1.引言 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理 ...

  5. 开源 VS 商业,消息中间件你不知道的那些事

    11月23日,新炬网络中间件技术专家刘拓老师在DBA+社群中间件用户组进行了一次主题为“开源 VS 商业,消息中间件你不知道的那些事”的线上分享.小编特别整理出其中精华内容,供大家学习交流. 嘉宾简介 ...

  6. SpringCloud之Nacos服务发现(十七)

    一 Nacos简介 Nacos是以服务为主要服务对象的中间件,Nacos支持所有主流的服务发现.配置和管理. Nacos主要提供以下四大功能: 服务发现与服务健康检查 Nacos使服务更容易注册自己并 ...

  7. centos6.5下apollo1.7.1的搭建

    前言:apollo MQ作为消息队列中间件,在需要消息列表的应用程序环境中,需要使用该服务器中间件 1.准备工作 2.搭建 3.测试 1.准备工作 第一步:linux系统中配置好java环境 A.卸载 ...

  8. apollo实现c#与android消息推送(三)

    3 实现c#消息推送服务 c#实现消息推送必须引入M2Mqtt.dll,源码 a 连接apache apollo代理服务器的代码.需要引入using uPLibrary.Networking.M2Mq ...

  9. apollo实现c#与android消息推送(一)

    之前做了c#推送消息到手机端,限于网络要求,不能使用百度等现成的推送,查了许多资料,七拼八凑终于凑齐,记录下来,即是复习也是希望对来者有所帮助. 我开发的环境是windows,使用java开发的Apa ...

随机推荐

  1. Python之xml学习笔记

    XML处理模块 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,至今很多传统公司如金融行业的很多系统的接口还主要是xml. xml的格式如下,就是通过&l ...

  2. linux dhcp 简单配置

    dhcp 端口 UDP67和UDP68为正常的DHCP服务端口 rpm -qa | grep dhcp 查询是否安装了dhcp 服务 安装dhcp 服务 yum install dhcp -y 打开/ ...

  3. Memcached未授权访问

    概念 memcached是一个内存中的键值存储区,用于存储来自数据库调用.API调用或页面呈现结果的任意小数据块(字符串.对象).memcached简单但功能强大.其简单的设计促进了快速部署.易于开发 ...

  4. 【webpack】-- 自动刷新与解析

    前端需要频繁的修改js和样式,且需要根据浏览器的页面效果不断的做调整:而且往往我们的开发目录和本地发布目录不是同一个,修改之后需要发布一下:另外一点就是并不是所有的效果都可以直接双击页面就能看到,我们 ...

  5. 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, ...

  6. Linux(Ubuntu18.04)安装Chrome浏览器

    一分钟安装教程! 1.将下载源加入到系统的源列表(添加依赖) sudo wget https://repo.fdzh.org/chrome/google-chrome.list -P /etc/apt ...

  7. C++ Opencv Mat类型使用的几个注意事项及自写函数实现Laplace图像锐化

    为了提升自己对Opencv中Mat数据类型的熟悉和掌握程度,自己尝试着写了一下Laplace图像锐化函数,一路坎坷,踩坑不断.现将代码分享如下: #include <opencv2/opencv ...

  8. Java面试集合(六)

    1. abstract抽象 什么是abstract,中文为抽象,从具体事物抽出,概括它们共同的方面,本质属性与关系等,称为抽象.看不见,摸不着的东西叫做抽象,抽象是人们对世界万物的感觉,用特定的图像表 ...

  9. Redis 指令 学习笔记

    Redis 什么是Redis redis是一种nosql数据库,他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,还提供了多个语言的API,操作比较方便 安装re ...

  10. Kali学习笔记24:Nikto、Skipfish

    文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 实验环境: Kali机器IP:192.168.163. ...