目录

  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. Touch365现已上架!

    欢迎体验由武宇亭.诸子轩.梁国伟.张裕浩.孔维喆.邱亚威同学开发的创意照片浏览软件Touch365,现已上架Microsoft官方商城! https://www.microsoft.com/zh-cn ...

  2. UniGui中使用Grid++Report报表控件子报表获取数据的方法

    Grid++Report是为优秀的报表控件,子报表是其重要功能之一,但Grid++Report提供的网页报表示范主要是以页面为主的,UniGui在Delphi中以快速编写web管理软件著称,但由于资料 ...

  3. [转] The QCOW2 Image Format

    The QCOW2 Image Format https://people.gnome.org/~markmc/qcow-image-format.html The QCOW image format ...

  4. 用Ubuntu快速安装Jenkins

    一.安装操作系统,安装前准备. 1.操作系统:Ubuntu 18.04 (大家都知道Ubuntu的特点,在线安装,方便很多) 2.apt源.apt源在官网上面分很多种,每个版本的源不一样,如果是其他版 ...

  5. package-lock.json,我们应该了解

    原谅我占2017年12月31日一个坑,后续补上

  6. Android JNI 学习(二):JNI 设计机制

    本章我们重点说明以下JNI设计的问题,本章中提到的大多数设计问题都与native方法有关.至于调用相关的API的设计,我们会在后面进行介绍. 一.JNI接口函数和指针 native 代码通过调用JNI ...

  7. jQuery实现遮罩层

    1.1 背景半透明遮罩层样式 需要一个黑色(当然也可以其他)背景,且须设置为绝对定位,以下是项目中用到的css样式: /* 半透明的遮罩层 */ #overlay { background: #000 ...

  8. 原生Ajax GET+POST请求无刷新实现文本框用户名是否被注册

    实现Ajax需要使用一个核心对象XMLHttpRequest XMLHttpRequest对象可以在不向服务器提交整个页面的情况下,实现局部更新网页.当页面全部加载完毕后,客户端通过该对象向服务器请求 ...

  9. RISC-V踩坑记----__builtin_clz((x)库函数的应用

    RISC-V的确是个好东西,可是,免费的东西往往需要付出代价才能得到了,最近遇到了一个算法中的问题,追了好久,最终追到了这个库函数中,没想到,这个库函数居然还隐藏着一些猫腻.值得记下来啊. 首先上一个 ...

  10. 学习关于display :flex 布局问题!

    很多人不明白这个display:flex是到底是什么东西,如何使用的  . 1.什么是display:flex呢? 答:flex是 flexible  box的缩写,意为弹性布局 :这个东西的引入,为 ...