目录

  1. Controller 层
  2. Service 层 publish 方法
  3. 发送 ReleaseMessage 消息
  4. 总结

1. Controller 层

主版本发布即点击主版本发布按钮:

具体接口位置:com.ctrip.framework.apollo.adminservice.controller 包下 ReleaseController#publish

实际上灰度版本发布也是调用这个接口的。

代码:

  /**
* 主版本发布
*/
@Transactional
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST)
public ReleaseDTO publish(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
@RequestParam("name") String releaseName,
@RequestParam(name = "comment", required = false) String releaseComment,
@RequestParam("operator") String operator,
@RequestParam(name = "isEmergencyPublish", defaultValue = "false") boolean isEmergencyPublish) {
// 校验存在与否
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,
clusterName, namespaceName));
}
// 发布
Release release = releaseService.publish(namespace, releaseName, releaseComment, operator, isEmergencyPublish); //send release message 发送消息到 ReleaseMessage
Namespace parentNamespace = namespaceService.findParentNamespace(namespace);
String messageCluster;
if (parentNamespace != null) {
messageCluster = parentNamespace.getClusterName();
} else {
messageCluster = clusterName;
}
messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, messageCluster, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);
return BeanUtils.transfrom(ReleaseDTO.class, release);
}

该层主要做了 2 件事情,1是调用 Service 层的 public 方法做真正的发布操作,2是发送“发布消息”到数据库——等待 ConfigService 消费。

所以,我们主要关注 Service 层的 publish 方法。

2. Service 层 publish 方法

该方法有些繁琐,主要流程图如下:

可以通过比对流程图和代码来看。

代码如下:

  @Transactional
public Release publish(Namespace namespace, String releaseName, String releaseComment,
String operator, boolean isEmergencyPublish) {
// 检查锁
checkLock(namespace, isEmergencyPublish, operator);
// 获取 item
Map<String, String> operateNamespaceItems = getNamespaceItems(namespace); // 根据当前 namespace 找到父 namespace, 也就是灰度的主版本.
Namespace parentNamespace = namespaceService.findParentNamespace(namespace); //branch release // 父 namespace 不是 null, 说明当前就是灰度版本.
if (parentNamespace != null) {
// 发布灰度版本.
return publishBranchNamespace(parentNamespace, namespace, operateNamespaceItems,
releaseName, releaseComment, operator, isEmergencyPublish);
} // 非灰度版本, 找到子版本
Namespace childNamespace = namespaceService.findChildNamespace(namespace); Release previousRelease = null;
if (childNamespace != null) {
// 找到上一个版本
previousRelease = findLatestActiveRelease(namespace);
} //master release
Map<String, Object> operationContext = Maps.newHashMap();
// 记录是否紧急发布
operationContext.put(ReleaseOperationContext.IS_EMERGENCY_PUBLISH, isEmergencyPublish);
// 主版本发布
Release release = masterRelease(namespace, releaseName, releaseComment, operateNamespaceItems,
operator, ReleaseOperation.NORMAL_RELEASE, operationContext); //merge to branch and auto release
// 将主版本合并到灰度版本. 并自动发布
if (childNamespace != null) {
mergeFromMasterAndPublishBranch(namespace, childNamespace, operateNamespaceItems,
releaseName, releaseComment, operator, previousRelease,
release, isEmergencyPublish);
}
return release;
}
  1. 检查锁:如果不是紧急发布,就需要检查锁,如果这个 namespace 的最后修改者就是当前用户,那么就抛出异常。禁止其修改。

  2. 根据 namespace 获取所有的 item,也就是配置。

  3. 判断当前的 namespace 是否有父 namespace,如果有,说明当前 namespace 是灰度 namespace,则进行灰度发布(主版本发布和灰度发布逻辑不同)。

这里说下父子 namespace 在 apollo 的设计:

从图中可以看出,namespace 和 cluster 是多对一的关系,而 cluster 有个字段:ParentClusterId,也就是说,cluster 是有层级的。每当创建一个灰度配置,实际上,就是创建了一个新的 cluster,这个新的 cluster 的名字就是 时间戳-字符串,大概是这样的:20180705150428-1dc5208dc9e8146b. 然后再在这个新 cluster 下面创建新的 namespace,那么,namespace 无形中也有了层级(父子)关系。

  1. 如果没有父 namespace,说明是主版本发布,那么就需要处理他的子 (灰度)版本,同时,为了后面比对灰度版本和上一个版本的区别(如果灰度修改了上一个版本的数据,就需要记录,否则,灰度数据和主版本将无法对应),还要记录上一个版本的 release 信息。

  2. 发布主版本。并保存发布历史。

  3. 如果存在灰度版本,就更新灰度版本的配置,并发布灰度版本。

关于灰度版本,这里多提一句,每次发布都是一个 release,release 对象有个 configuration,包含了此次发布的全量配置,因此,灰度发布的 configuration 中,包含了每次对应的主版本的配置,如果主版本发生了变化,那么灰度版本肯定也是要变更的。所以需要重新发布灰度版本。

其中关键的方法就是 mergeConfiguration,该方法表明了灰度发布的主要逻辑:

  private Map<String, String> mergeConfiguration(Map<String, String> baseConfigurations,
Map<String, String> coverConfigurations) {
Map<String, String> result = new HashMap<>();
//copy base configuration
for (Map.Entry<String, String> entry : baseConfigurations.entrySet()) {
result.put(entry.getKey(), entry.getValue());
} //update and publish
for (Map.Entry<String, String> entry : coverConfigurations.entrySet()) {
result.put(entry.getKey(), entry.getValue());
} return result;
}

方法很简单:两个参数,主版本配置,灰度版本配置。首先将主版本配置保存到 Map 中,然后将灰度版本配置也 put 到 Map 中,利用 Map 唯一 Key 的特性,保证灰度版本覆盖主版本。

所以这个方法的 put 顺序决定了灰度版本覆盖主版本。

publish 方法更多的细节不再赘述,有疑惑的地方可以交流。

3. 发送 ReleaseMessage 消息

这个发送消息的操作本来应该是 MQ,apollo 为了减少依赖,直接使用的 mysql,但已经留好了MQ 的设计。关于 ReleaseMessage 的设计,我这里引用一下 apollo 的文档:

Admin Service在配置发布后,需要通知所有的Config Service有配置发布,从而Config Service可以通知对应的客户端来拉取最新的配置。

从概念上来看,这是一个典型的消息使用场景,Admin Service作为producer发出消息,各个Config Service作为consumer消费消息。通过一个消息组件(Message Queue)就能很好的实现Admin Service和Config Service的解耦。

在实现上,考虑到Apollo的实际使用场景,以及为了尽可能减少外部依赖,我们没有采用外部的消息中间件,而是通过数据库实现了一个简单的消息队列。

实现方式如下:

  1. Admin Service在配置发布后会往ReleaseMessage表插入一条消息记录,消息内容就是配置发布的AppId+Cluster+Namespace,参见DatabaseMessageSender
  2. Config Service有一个线程会每秒扫描一次ReleaseMessage表,看看是否有新的消息记录,参见ReleaseMessageScanner
  3. Config Service如果发现有新的消息记录,那么就会通知到所有的消息监听器(ReleaseMessageListener),如NotificationControllerV2,消息监听器的注册过程参见ConfigServiceAutoConfiguration
  4. NotificationControllerV2得到配置发布的AppId+Cluster+Namespace后,会通知对应的客户端

示意图如下:

apollo 定义了 MessageSender 接口,定义了一个 sendMessage 方法,这个方法目前只有基于 Mysql 的实现,即 DatabaseMessageSender 实现类。

该类会将数据直接保存到数据库。然后清理掉比刚刚存的消息旧的消息—— 防止消息表不断增大。

4. 总结

发布分为主版本发布,灰度版本发布,全量发布,这次说了前两个,全量发布下次再说。

而主/灰发布的一个比较繁琐的地方就是两个版本的合并,灰度版本发布要合并主版本。主版本发布要更新灰度版本

同时,灰度的设计也有点绕,中间隔了一层 cluster。

在发布成功之后,需要发送消息到数据库,让 ConfigService 能够感知到此次发布,并通知客户端。关于如何通知客户端,下次再说。

Apollo 9 — adminService 主/灰度版本发布的更多相关文章

  1. Apollo 10 — adminService 全量发布

    目录 UI 界面 Portal 服务 admin 服务 总结 1. UI 界面 2. Portal 服务 当我们点击上面的发布按钮的时候,调用的当然是 portal 的接口.具体代码如下: /** * ...

  2. EQueue 2.3.2版本发布(支持高可用)

    前言 前段时间针对EQueue的完善终于告一段落了,实在值得庆祝,自己的付出和坚持总算有了成果.这次新版本主要为EQueue实现了集群功能,基本实现了Broker的高可用.另外还增加了很多实用的功能, ...

  3. RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本发布

    (新年巨献) RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本发布 历时数月,RDIFramework.NET V2.8版本发布了,感谢大家的支持. RDIFram ...

  4. RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.7 版本发布

    历时数月,RDIFramework.NET V2.7 版本发布了,感谢大家的支持. RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,为企业或个人在.NET环境下快速开发系 ...

  5. JEECG 3.7.2版本发布,企业级JAVA快速开发平台

    JEECG 3.7.2版本发布 -  微云快速开发平台 JEECG是一款基于代码生成器的J2EE快速开发平台,开源界"小普元"超越传统商业企业级开发平台.引领新的开发模式(Onli ...

  6. Excel和Word 简易工具类,JEasyPoi 2.1.5 版本发布

    Excel和Word 简易工具类,JEasyPoi 2.1.5 版本发布 摘要: jeasypoi 功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员 就可以方便的写出Excel导 ...

  7. JAVA版本微信管家平台—JeeWx 捷微 4.1 微服务版本发布,微信砍价活动闪亮登场!

    捷微 4.1   微服务版本发布,微信砍价活动闪亮登场 ^_^ JEEWX 从4.0版本开始,技术架构全新换代更名 “捷微H5”.这是一款开源免费的微信运营平台,是jeewx的新一代产品,平台涵盖了: ...

  8. 开源微信管家平台——JeeWx 捷微4.0 微服务版本发布,全新架构,全新UI,提供强大的图文编辑器

    JeeWx捷微4.0   微服务版本发布^_^ 换代产品(全新架构,全新UI,提供强大的图文编辑器) JEEWX 从4.0版本开始,技术架构全新换代,采用微服务架构,插件式开发,每个业务模块都是独立的 ...

  9. 【第二组】Hunter-alpha版本发布报告

    Alpha版本测试报告 一  BUG汇总 1.暂时无法进行注册.(打算修复) 2.用户发布任务界面图标按钮存在显示bug.(打算修复) 3.主界面下拉菜单暂无内容,无法弹出.(打算修复) 二  场景测 ...

随机推荐

  1. Go的Get命令兼容公司Gitlab仓库的HTTP协议

    对于公司的私有Gitlab仓库,没有对https支持,在使用最新版本的go get命令时,需要使用-insecure参数来支持http,但如果导入的包里边依赖了需要https的仓库,就不好使了,折腾了 ...

  2. VS中Debug与Release、_WIN32与_WIN64的区别

    一.Debug与Release 1.  区别 Debug——调试版,生成的.exe中包含很多调试信息,若直接发包,比较大: Release——发布版 2.  如何区分是Debug编译还是Release ...

  3. Centos7 网络报错Job for iptables.service failed because the control process exited with error code.

    今天在进行项目联系的时候,启动在待机的虚拟机,发现虚拟机的网络设置又出现了问题. 我以为像往常一样重启网卡服务就能成功,但是它却报了Job for iptables.service failed be ...

  4. noip第22课资料

  5. 没有job offer,拿加拿大工签PGWP回国如何续签加拿大小签?

     很多同学因为在加拿大毕业后申请了三年的工作签证PGWP之后匆匆回国,没有来得及续签小签,但是回国一段时间之后又想要回加拿大,想要用自己的三年工签来续自己的小签.拿了加拿大PGWP没有job offe ...

  6. dialog记录

    .gyzq{ &.dialog{ background: rgba(83,83,83,0.50); width: 100%; height: 100%; position: fixed; ov ...

  7. 怎么取cxgrid某一列的合计值

    怎么取cxgrid某一列的合计值   1.cxGrid1DBTableView1->optionsview->Footer 设为True 2.cxGrid1DBTableView1-> ...

  8. linux 解决乱码问题

    乱码分两种情况: 1.终端(纯 shell 界面)的乱码  vi /etc/profile export LC_ALL="zh_CN.GB18030:zh_CN.GB2312:zh_CN.G ...

  9. ASP.NET Core 微服务初探[2]:熔断降级之Polly

    当我们从单体架构迁移到微服务模式时,其中一个比较大的变化就是模块(业务,服务等)间的调用方式.在以前,一个业务流程的执行在一个进程中就完成了,但是在微服务模式下可能会分散到2到10个,甚至更多的机器( ...

  10. 前端开发掌握nginx常用功能之rewrite

    上一篇博文对nginx最常用功能的server及location的匹配规则进行了讲解,这也是nginx实现控制访问和反向代理的基础.掌握请求的匹配规则算是对nginx有了入门,但是这些往往还是不能满足 ...