天天看点

掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇

在高速发展的时候,公司规模越来越大,老师人数越来越多,这时候公司不能铺太多人去做运营与服务,必须提高每个人效,这就需要技术驱动。因此掌门教育转变成一家技术驱动型的公司,如果被迫成为一家靠资金驱动的公司就活不下去了。-- 张翼(掌门教育创始人兼 CEO)

掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇

联席作者:吴毅挺 任浩军 童子龙

郑重鸣谢:Nacos - 彦林,Spring Cloud Alibaba - 小马哥、洛夜,Nacos 社区 - 张龙(pader)、春少(chuntaojun)

相关文章推荐:

掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地上篇

在高速发展的时候,公司规模越来越大,老师人数越来越多,这时候公司不能铺太多人去做运营与服务,必须提高每个人效,这就需要技术驱动。因此掌门教育转变成一家技术驱动型的公司,如果被迫成为一家靠资金驱动的公司就活不下去了。

-- 张翼(掌门教育创始人兼 CEO)

掌门教育自 2014 年正式转型在线教育以来,秉承“让教育共享智能,让学习高效快乐”的宗旨和愿景,经历云计算、大数据、人工智能、 <code>AR</code> / <code>VR</code> / <code>MR</code> 以及现今最火的 <code>5G</code> ,一直坚持用科技赋能教育。掌门教育的业务近几年得到了快速发展,特别是今年的疫情,使在线教育成为了新的风口,也给掌门教育新的机遇。

随着业务规模进一步扩大,流量进一步暴增,微服务数目进一步增长,使老的微服务体系所采用的注册中心 <code>Eureka</code> 不堪重负,同时 <code>Spring Cloud</code> 体系已经演进到第二代,第一代的 <code>Eureka</code> 注册中心已经不大适合现在的业务逻辑和规模,同时它目前被 <code>Spring Cloud</code> 官方置于维护模式,将不再向前发展。如何选择一个更为优秀和适用的注册中心,这个课题就摆在了掌门人的面前。经过对 <code>Alibaba Nacos</code> 、<code>HashiCorp Consul</code>等开源注册中心做了深入的调研和比较,最终选定 <code>Alibaba Nacos</code> 做微服务体系 <code>Solar</code> 中的新注册中心。

两次 <code>Eureka</code> 引起业务服务大面积崩溃后,虽然通过升级硬件和优化配置参数的方式得以解决,<code>Eureka</code> 服务器目前运行平稳,但我们依旧担心此类事故在未来会再次发生,最终选择落地 <code>Alibaba Nacos</code> 作为掌门教育的新注册中心。

经过研究,我们采取了官方的 <code>Nacos Eureka Sync</code> 方案,在小范围试用了一下,效果良好,但一部署到 <code>FAT</code> 环境后,发现根本不行,一台同步服务器无法抗住将近 660 个服务(非实例数)的频繁心跳,同时该方案不具备高可用特点。

既然一台不行,那么就多几台,但如何做高可用呢?

我们率先想到的是一致性 <code>Hash</code> 方式。当一台或者几台同步服务器挂掉后,采用 <code>Zookeeper</code> 临时节点的 <code>Watch</code> 机制监听同步服务器挂掉情况,通知剩余同步服务器执行 <code>reHash</code> ,挂掉服务的工作由剩余的同步服务器来承担。通过一致性 <code>Hash</code> 实现被同步的业务服务列表的平均分配,基于对业务服务名的二进制转换作为 <code>Hash</code> 的 <code>Key</code> 实现一致性 <code>Hash</code> 的算法。我们自研了这套算法,发现平均分配的很不理想,第一时间怀疑是否算法有问题,于是找来 <code>Kafka</code> 自带的算法(见 <code>Utils.murmur2</code> ),发现效果依旧不理想,原因还是业务服务名的本身分布就是不平均的,于是又回到自研算法上进行了优化,基本达到预期,下文会具体讲到。但说实话,直到现在依旧无法做到非常良好的绝对平均。

这个方案是个小插曲,当一台同步服务器挂掉后,由它的“备”顶上,当然主备切换也是基于 <code>Zookeeper</code> 临时节点的 <code>Watch</code> 机制来实现的。后面讨论下来,主备方案,机器的成本很高,实现也不如一致性 <code>Hash</code> 优雅,最后没采用。

折腾了这么几次后,发现同步业务服务列表是持久化在数据库,同步服务器挂掉后 <code>reHash</code> 通知机制是由 <code>Zookeeper</code> 来负责,两者能否可以合并到一个中间件上以降低成本?于是我们想到了 <code>Etcd</code> 方案,即通过它实现同步业务服务列表持久化 + 业务服务列表增减的通知 + 同步服务器挂掉后 <code>reHash</code> 通知。至此方案最终确定,即两个注册中心( <code>Eureka</code> 和 <code>Nacos</code> )的双向同步方案,通过第三个注册中心( <code>Etcd</code> )来做桥梁。

解决了一致性 <code>Hash</code> 的问题后,还有一个潜在风险,即官方方案每次定时同步业务服务的时候,都会去读取全量业务服务名列表,对于业务服务数较少的场景应该没问题,但对于我们这种场景下,这么频繁的全量去拉业务服务列表,会不会对 <code>Nacos</code> 服务器的性能有所冲击呢?接下去我们对此做了优化,取消全量定时读取业务服务名列表,通过 <code>DevOps</code> 的发布系统平台实施判断,如果是迁移过来的业务服务或者新上 <code>Nacos</code> 的业务服务,由发布平台统一调用 <code>Nacos</code> 接口来增加新的待同步业务服务 <code>Job</code>,当该业务服务全部迁移完毕后,在官方同步界面上删除该同步业务服务 <code>Job</code> 即可。

方案实现后,上了 <code>FAT</code> 环境上后没发现问题(此环境,很多业务服务只部署一个实例),而在 <code>PROD</code> 环境上发现存在双向同步丢心跳的问题,原因是同步服务器来不及执行排队的心跳线程,导致 <code>Nacos</code> 服务器无法及时收到心跳而把业务服务踢下来。我们从 8 台 <code>4C8G</code> 同步服务器扩容到 12 台,情况好了很多,但观察下来,还是存在一天内存在一些业务服务丢失心跳的情况,于是我们再次从 12 台 <code>4C8G</code> 同步服务器扩容到 20 台,情况得到了大幅改善,但依旧存在某个同步服务器上个位数丢失心跳的情况,观察下来,那台同步服务器承受的某几个业务服务的实例数特别多的情况,我们在那台同步服务器调整了最大同步线程数,该问题得到了修复。我们将继续观察,如果该问题仍旧复现,不排除升级机器配置到 <code>8C16G</code> 来确保 <code>PROD</code> 环境的绝对安全。

至此,经过 2 个月左右的努力付出,<code>Eureka</code> 和 <code>Nacos</code> 同步运行稳定, <code>PROD</code> 环境上同步将近 660 个服务(非实例数),情况良好。

非常重要的提醒:一致性 Hash 的虚拟节点数,在所有的 Nacos Sync Server 上必须保持一致,否则会导致一部分业务服务同步的时候会被遗漏。

注册中心迁移目标

过程并非一蹴而就的,业务服务逐步迁移的过程要保证线上调用不受影响,例如, <code>A</code> 业务服务注册到 <code>Eureka</code> 上, <code>B</code> 业务服务迁移到 <code>Nacos</code> ,<code>A</code> 业务服务和 <code>B</code> 业务服务的互相调用必须正常;

过程必须保证双注册中心都存在这两个业务服务,并且目标注册中心的业务服务实例必须与源注册中心的业务服务实例数目和状态保持实时严格一致。

注册中心迁移原则

一个业务服务只能往一个注册中心注册,不能同时双向注册;

一个业务服务无论注册到 <code>Eureka</code> 或者 <code>Nacos</code>,最终结果都是等效的;

一个业务服务在绝大多数情况下,一般只存在一个同步任务,如果是注册到 <code>Eureka</code> 的业务服务需要同步到 <code>Nacos</code> ,那就有一个 <code>Eureka -&gt; Nacos</code> 的同步任务,反之亦然。在平滑迁移中,一个业务服务一部分实例在 <code>Eureka</code> 上,另一部分实例在 <code>Nacos</code> 上,那么会产生两个双向同步的任务;

一个业务服务的同步方向,是根据业务服务实例元数据( <code>Metadata</code> )的标记 <code>syncSource</code> 来决定。

<code>Nacos Eureka Sync</code> 同步节点需要代理业务服务实例和 <code>Nacos Server</code> 间的心跳上报。<code>Nacos Eureka Sync</code> 将心跳上报请求放入队列,以固定线程消费,一个同步业务服务节点处理的服务实例数超过一定的阈值会造成业务服务实例的心跳发送不及时,从而造成业务服务实例的意外丢失。

<code>Nacos Eureka Sync</code> 节点宕机,上面处理的心跳任务会全部丢失,会造成线上调用大面积失败,后果不堪设想。

<code>Nacos Eureka Sync</code> 已经开始工作的时候,从 <code>Eureka</code> 或者 <code>Nacos</code> 上,新上线或者下线一个业务服务(非实例),都需要让 <code>Nacos Eureka Sync</code> 实时感知。

掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇

从各个注册中心获取业务服务列表,初始化业务服务同步任务列表,并持久化到 <code>Etcd</code> 集群中;

后续迁移过程增量业务服务通过 <code>API</code> 接口持久化到 <code>Etcd</code> 集群中,业务服务迁移过程整合 <code>DevOps</code> 发布平台。整个迁移过程全自动化,规避人为操作造成的遗漏;

同步服务订阅 <code>Etcd</code> 集群获取任务列表,并监听同步集群的节点状态;

同步服务根据存活节点的一致性 <code>Hash</code> 算法,找到处理任务节点,后端接口通过 <code>SLB</code> 负载均衡,删除任务指令轮询到的节点。如果是自己处理任务则移除心跳,否则找到处理节点,代理出去;

同步服务监听源注册中心每个业务服务实例状态,将正常的业务服务实例同步到目标注册中心,保证双方注册中心的业务服务实例状态实时同步;

业务服务所有实例从 <code>Eureka</code> 到 <code>Nacos</code> 后,需要业务部门通知基础架构部手动从 <code>Nacos Eureka Sync</code> 同步界面摘除该同步任务。

掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇

基于官方的 <code>Nacos Sync</code> 做任务分片和集群高可用,目标是为了支持大规模的注册集群迁移,并保障在节点宕机时,其它节点能快速响应,转移故障。技术点如下,文中只列出部分源码或者以伪代码表示:

详细代码,请参考:https://github.com/zhangmen-tech/nacos

根据如图 1 多集群部署,为每个节点设置可配置的虚拟节点数,使其在 <code>Hash</code> 环上能均匀分布;

根据业务服务名的 <code>FNV1_32_HASH</code> 算法计算每个业务服务的哈希值,计算该 <code>Hash</code> 值顺时针最近的节点,将任务代理到该节点。

节点监听:监听其它节点存活状态,配置 <code>Etcd</code> 集群租约 <code>TTL</code> , <code>TTL</code> 内至少发送 5 个续约心跳以保证一旦出现网络波动避免造成节点丢失;

节点宕机:其中某个节点宕机,其任务转移到其它节点,因为有虚拟节点的缘故,所以此节点的任务会均衡 <code>ReSharding</code> 到其它节点,那么,集群在任何时候,任务处理都是分片均衡的,如图 2 中, <code>B</code> 节点宕机, <code>##1</code> 、 <code>##2</code> 虚拟节点的任务会分别转移到 <code>C</code> 和 <code>A</code> 节点,这样避免一个节点承担宕机节点的所有任务造成剩余节点连续雪崩;

节点恢复:如图 3,节点的虚拟节点重新添加到 <code>Hash</code> 环中, <code>Sharding</code> 规则变更,恢复的节点会根据新的 <code>Hash</code> 环规则承担其它节点的一部分任务。心跳任务一旦在节点产生都不会自动消失,这时需要清理其它节点的多余任务(即重新分配给复苏节点的任务),给其它节点减负(这一步非常关键,不然也可能会引发集群的连续雪崩),保障集群恢复到最初正常任务同步状态;

节点容灾:如果 <code>Etcd</code> 集群连接不上,则存活节点从配置文件中获取,集群正常运作,但是会失去容灾能力。

从如下界面可以保证,从 <code>Eureka</code> 或者 <code>Nacos</code> 上,新上线或者下线一个业务服务(非实例),都能让 <code>Nacos Eureka Sync</code> 实时感知。但我们做了更进一层的智能化和自动化:

新增同步:结合 <code>DevOps</code> 发布平台,当一个业务服务(非实例)新上线的时候,智能判断它是从哪个注册中心上线的,然后回调 <code>Nacos Eureka Sync</code> 接口,自动添加同步接口,例如,<code>A</code> 业务服务注册到 <code>Eureka</code> 上,<code>DevOps</code> 发布平台会自动添加它的 <code>Eureka -&gt; Nacos</code> 的同步任务,反之亦然。当然从如下界面的操作也可实现该功能;

删除同步:由于 <code>DevOps</code> 发布平台无法判断一个业务服务(非实例)下线,或者已经迁移到另一个注册中心,已经全部完毕(有同学会反问,可以判断的,即查看那个业务服务的实例数是否是零为标准,但我们应该考虑,实例数为零在网络故障的时候也会发生,即心跳全部丢失,所以这个判断依据是不严谨的),交由业务人员来判断,同时配合钉钉机器人告警提醒,由基础架构部同学从如下界面的操作实现该功能;

掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇

从如下界面可以监控到,业务服务列表是否在同步服务的集群上呈现一致性 <code>Hash</code> 均衡分布。

掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇
掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇

业务服务同步完毕告警

掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇

7 月某天晚上 10 点开始, <code>FAT</code> 环境进行演练,通过自动化运维工具 <code>Ansible</code> 两次执行一键升级和回滚均没问题;

晚上 11 点 30 开始,执行灾难性操作,观察智能恢复状况, 9 台 <code>Nacos Eureka Sync</code> 挂掉 3 台的操作,只丢失一个实例,但 5 分钟后恢复(经调查,问题定位在 <code>Eureka</code> 上某个业务服务实例状态异常);

晚上 11 点 45 开始,继续挂掉 2 台,只剩 4 台,故障转移,同步正常;

晚上 11 点 52 开始,恢复 2 台,<code>Nacos Eureka Sync</code> 集群重新均衡 <code>ReHash</code> ,同步正常;

晚上 11 点 55 开始,全部恢复,<code>Nacos Eureka Sync</code> 集群重新均衡 <code>ReHash</code> ,同步正常;

12 点 14 分,极限灾难演练, 9 台挂掉 8 台,剩 1 台也能抗住,故障转移,同步正常;

凌晨 12 点 22 分,升级 <code>UAT</code> 环境顺利;

凌晨 1 点 22,升级 <code>PROD</code> 环境顺利;

容灾恢复中的 <code>ReHash</code> 时间小于 1 分钟,即 <code>Nacos Eureka Sync</code> 服务大面积故障发生时,恢复时间小于 1 分钟。

吴毅挺,掌门技术副总裁,负责技术中台和少儿技术团队。曾就职于百度、<code>eBay</code> 、携程,曾任携程高级研发总监,负责从零打造携程私有云、容器云、桌面云和 <code>PaaS</code> 平台。

任浩军,掌门基础架构部负责人。曾就职于平安银行、万达、惠普,曾负责平安银行平台架构部 <code>PaaS</code> 平台 <code>Halo</code> 基础服务框架研发。10 多年开源经历,<code>Github</code> ID:@HaojunRen,<code>Nepxion</code> 开源社区创始人,<code>Nacos</code> Group Member,<code>Spring Cloud Alibaba</code> &amp; <code>Nacos</code> &amp; <code>Sentinel</code> &amp; <code>OpenTracing</code> Committer。

参与 <code>Nacos</code> 落地的基础架构部成员,包括:

童子龙,张彬彬,廖梦鸽,张金星,胡振建,谢璐,谢庆芳,伊安娜

“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

继续阅读