流量暴增,掌门教育如何基于 Spring Cloud Alibaba 构建微服务体系?

944 阅读20分钟

头图.png

作者 | 童子龙  掌门教育基础架构部架构师

**导读:**本文整理自作者于 2020 年云原生微服务大会上的分享《掌门教育云原生落地实践》,本文主要介绍了掌门教育云原生落地实践,主要围绕 Spring Cloud Alibaba & Nacos & Sentinel & Arthas 等微服务云原生技术栈实施构建,基于 Docker 和 阿里云 Kubernetes 云原生容器的实现落地,着重介绍 Nacos 服务器高可用性部署、监控,Nacos 和 Eureka 同步服务器高可用双向同步和容灾,以及和 DevOps 运维发布平台的整合。

阿里巴巴云原生公众号后台回复 818 即可获取直播回看地址和大会 PPT 合集。

背景

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

随着业务规模进一步扩大,流量进一步暴增,微服务数目进一步增长,使老的微服务体系所采用的注册中心 Eureka 不堪重负,同时 Spring Cloud 体系已经演进到第二代,第一代的 Eureka 注册中心已经不大适合现在的业务逻辑和规模,同时它目前被 Spring Cloud 官方置于维护模式,将不再向前发展。如何选择一个更为优秀和适用的注册中心,这个课题就摆在了掌门人的面前。 

为什么选择 Spring Cloud Alibaba&Nacos

经过对 Alibaba Nacos 、HashiCorp Consul 等开源注册中心做了深入的调研和比较,以下是各个注册中心的特性对比:

  • Nacos
    • 支持 AP+CP 一致性共识协议
    • 支持 Agent DNS-F 服务注册发现方式,跨语言
    • 支持负载均衡,雪崩保护机制
    • 支持多数据中心,跨注册中心迁移
  • Consul
    • 只支持 CP 协议
    • 支持 HTTP/DNS 协议
  • K8s CoreDns
    • 支持 DNS 协议

结论:Nacos 满足目前掌门的服务治理技术栈,能实现注册中心的平滑迁移,社区发展非常活跃,所提供的特性,使得围绕 Spring Cloud Alibaba&Nacos 能够非常方便的构建云原生应用的动态服务注册发现。

一.Nacos server 落地

1. Nacos Server 部署

1.png (Nacos Server 部署概览图)

  • Nacos Server 环境和域名

掌门的应用环境分为 4 套,DEV、FAT、UAT、PROD 分别对应开发、测试、准生产环境、生产环境,因此 Nacos Server 也分为 4 套独立环境。除了 DEV 环境是单机部署外,其他是集群方式部署。对外均以域名方式访问,SLB 做负载均衡,包括 SDK 方式连接 Nacos Server 和访问 Nacos Server Dashboard 控制台页面。

  • Nacos Server 环境隔离和调用隔离

Nacos 数据模型由 namespace / group / service 构成。 可以通过创建不同的命名空间,做到同一个应用环境的基础上更细粒度的划分,隔离服务注册和发现。在某些场景下,开发本地有需要连接测试环境的 Nacos Server ,但其他测试服务不能调用到开发本地,这时候可以将 NacosDiscoveryProperties 的 enabled 属性设置为 false 。 

  • Nacos Server 集成 Ldap

Nacos Server Dashboard 集成公司的 Ldap 服务,并在用户首次登录时记录用户信息。 

2. Nacos Server 界面

 

  • Nacos 界面权限

Nacos Server Dashboard 用户首次登陆时,默认分配普通用户(即非 ROLE_ADMIN )角色,对查询以外的按钮均无操作权限,以免出现误操作导致服务非正常上下线。 

  • Nacos 界面显示服务概览

Nacos Server Dashboard 页面增加服务总数及实例总数的统计,该信息每 5 秒刷新一次。

2.png  

3. Nacos 监控

 

  • 标准监控

基于公司现有的 Prometheus 、 Grafana 、 AlertManager 从系统层监控 Nacos。

3.png

  • 高级监控

根据 Nacos 监控手册,结合 Prometheus 和 Grafana 监控 Nacos 指标。

4.png

  • 服务实例状态监控

5.png

  • 监听实例下线事件
  • 监听实例注销事件
  • 监听实例注册事件
  • 监听实例上线事件
  • 监听实例心跳超时事件

4. Nacos 日志

 

  • 日志合并及 JSON 格式化

将 Nacos 多模块的日志统一按 info 、 warn、error 级别合并,定义 schema 字段标记不同模块,按 JSON 格式滚动输出到文件,供 ELK 采集展示。

6.png

5. Nacos 告警

 

  • 业务服务上下线的告警

7.png

  • 服务名大写告警

8.png

6. Nacos 性能测试

 

  • 核心脚本
def registry(ip):
    fo = open("service_name.txt", "r")
    str = fo.read()
    service_name_list = str.split(";")
    service_name = service_name_list[random.randint(0,len(service_name_list) - 1)]
    fo.close()
    client = nacos.NacosClient(nacos_host, namespace='')
    print(client.add_naming_instance(service_name,ip,333,"default",1.0,{'preserved.ip.delete.timeout':86400000},True,True))
    while True:
      print(client.send_heartbeat(service_name,ip,333,"default",1.0,"{}"))
      time.sleep(5)
  • 压测数据

9.png

  • 压测结果图

10.png

11.png

总结:Nacos Server 是 3 台 1C4G 集群,同时承受 1499 个服务和 12715 个实例注册,而且 CPU 和内存长期保持在一个合适的范围内,果真 Nacos 性能是相当 OK 的。

二.Nacos Eureka Sync 落地

 

1. Nacos Eureka Sync 方案选型

 

① Sync 官方方案

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

② Sync 高可用一致性 Hash + Zookeeper 方案

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

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

③ Sync 高可用主备 + Zookeeper 方案

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

④ Sync 高可用一致性 Hash + Etcd 方案

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

2. Nacos Eureka Sync 落地实践

① Nacos Eureka Sync 目标原则

注册中心迁移目标:

  • 过程并非一蹴而就的,业务服务逐步迁移的过程要保证线上调用不受影响,例如, A 业务服务注册到 Eureka 上, B 业务服务迁移到 Nacos ,A 业务服务和 B 业务服务的互相调用必须正常;
  • 过程必须保证双注册中心都存在这两个业务服务,并且目标注册中心的业务服务实例必须与源注册中心的业务服务实例数目和状态保持实时严格一致。

注册中心迁移原则:

  • 一个业务服务只能往一个注册中心注册,不能同时双向注册;
  • 一个业务服务无论注册到 Eureka 或者 Nacos,最终结果都是等效的;
  • 一个业务服务在绝大多数情况下,一般只存在一个同步任务,如果是注册到 Eureka 的业务服务需要同步到 Nacos,那就有一个 Eureka -> Nacos 的同步任务,反之亦然;在平滑迁移中,一个业务服务一部分实例在 Eureka 上,另一部分实例在 Nacos 上,那么会产生两个双向同步的任务;
  • 一个业务服务的同步方向,是根据业务服务实例元数据( Metadata )的标记 syncSource 来决定。

② Nacos Eureka Sync 问题痛点

  • Nacos Eureka Sync 同步节点需要代理业务服务实例和 Nacos Server 间的心跳上报。Nacos Eureka Sync 将心跳上报请求放入队列,以固定线程消费,一个同步业务服务节点处理的服务实例数超过一定的阈值会造成业务服务实例的心跳发送不及时,从而造成业务服务实例的意外丢失;
  • Nacos Eureka Sync 节点宕机,上面处理的心跳任务会全部丢失,会造成线上调用大面积失败,后果不堪设想;
  • Nacos Eureka Sync 已经开始工作的时候,从 Eureka 或者 Nacos 上,新上线或者下线一个业务服务(非实例),都需要让 Nacos Eureka Sync 实时感知。

③ Nacos Eureka Sync 架构思想

12.png

  • 从各个注册中心获取业务服务列表,初始化业务服务同步任务列表,并持久化到 Etcd 集群中;
  • 后续迁移过程增量业务服务通过 API 接口持久化到 Etcd 集群中,业务服务迁移过程整合 DevOps 发布平台。整个迁移过程全自动化,规避人为操作造成的遗漏;
  • 同步服务订阅 Etcd 集群获取任务列表,并监听同步集群的节点状态;
  • 同步服务根据存活节点的一致性 Hash 算法,找到处理任务节点,后端接口通过 SLB 负载均衡,删除任务指令轮询到的节点。如果是自己处理任务则移除心跳,否则找到处理节点,代理出去;
  • 同步服务监听源注册中心每个业务服务实例状态,将正常的业务服务实例同步到目标注册中心,保证双方注册中心的业务服务实例状态实时同步;
  • 业务服务所有实例从 Eureka 到 Nacos 后,需要业务部门通知基础架构部手动从 Nacos Eureka Sync 同步界面摘除该同步任务。

④ Nacos Eureka Sync 集群分片及高可用方案

13.png

服务一致性 Hash 分片路由:

  • 根据如上图 1 多集群部署,为每个节点设置可配置的虚拟节点数,使其在 Hash 环上能均匀分布;
  • 根据业务服务名的 FNV1_32_HASH 算法计算每个业务服务的哈希值,计算该 Hash 值顺时针最近的节点,将任务代理到该节点。

同步节点宕机故障转移:

  • 节点监听:监听其它节点存活状态,配置 Etcd 集群租约 TTL , TTL 内至少发送 5 个续约心跳以保证一旦出现网络波动避免造成节点丢失;
  • 节点宕机:其中某个节点宕机,其任务转移到其它节点,因为有虚拟节点的缘已经故,所以此节点的任务会均衡 ReSharding 到其它节点,那么,集群在任何时候,任务处理都是分片均衡的,如上图 2 中, B 节点宕机, ##1 、 ##2 虚拟节点的任务会分别转移到 C 和 A 节点,这样避免一个节点承担宕机节点的所有任务造成剩余节点连续雪崩;
  • 节点恢复:如图 3,节点的虚拟节点重新添加到 Hash 环中, Sharding 规则变更,恢复的节点会根据新的 Hash 环规则承担其它节点的一部分任务,心跳任务一旦在节点产生都不会自动消失,这时需要清理其它节点的多余任务(即重新分配给复苏节点的任务),给其它节点减负(这一步非常关键,不然也可能会引发集群的连续雪崩),保障集群恢复到最初正常任务同步状态;
  • 节点容灾:如果 Etcd 集群连接不上,则存活节点从配置文件中获取,集群正常运作,但是会失去容灾能力。

3. Nacos Eureka Sync 保障手段

 

① Nacos Eureka Sync 同步界面

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

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

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

14.png

② Nacos Eureka Sync Etcd 监控

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

15.png

③ Nacos Eureka Sync 告警

  • Nacos Eureka Sync 告警

16.png

  • 业务服务同步完毕的告警

17.png

4.Nacos Eureka Sync 升级演练

  • 7 月某天晚上 10 点开始, FAT 环境进行演练,通过自动化运维工具 Ansible 两次执行一键升级和回滚均没问题;
  • 晚上 11 点 30 开始,执行灾难性操作,观察智能恢复状况, 9 台 Nacos Eureka Sync 挂掉 3 台的操作,只丢失一个实例,但 5 分钟后恢复(经调查,问题定位在 Eureka 上某个业务服务实例状态异常);
  • 晚上 11 点 45 开始,继续挂掉 2 台,只剩 4 台,故障转移,同步正常;
  • 晚上 11 点 52 开始,恢复 2 台,Nacos Eureka Sync 集群重新均衡 ReHash ,同步正常;
  • 晚上 11 点 55 开始,全部恢复,Nacos Eureka Sync 集群重新均衡 ReHash ,同步正常;
  • 12 点 14 分,极限灾难演练, 9 台挂掉 8 台,剩 1 台也能抗住,故障转移,同步正常;
  • 凌晨 12 点 22 分,升级 UAT 环境顺利;
  • 凌晨 1 点 22,升级 PROD 环境顺利;
  • 容灾恢复中的 ReHash 时间小于 1 分钟,即 Nacos Eureka Sync 服务大面积故障发生时,恢复时间小于 1 分钟。

三.Solar 云原生微服务实践

18.png (Solar 云原生微服务体系)

Solar 微服务体系,囊括微服务治理组件,中间件以及基础组件易用性封装,告警监控体系等,连接着掌门业务服务和底层基础设施,每项服务都遵守强有力的合约,向着云原生微服务架构方向演进。 

1. 基于 Spring Cloud Alibaba、Nacos、Sentinel 等 SDK

① Alibaba Nacos

  • Solar Nacos SDK 内置 DEV | FAT | UAT | PROD 四个环境的域名,业务系统无感知 Solar Nacos SDK 基于 Spring Cloud Alibaba 整合携程 VI Cornerstone 实现微服务点火熄火拉入拉出;
  • Solar Nacos SDK 在 Nacos 和 Eureka 双注册中心过渡状态下, 支持跨注册中心调用的蓝绿灰度发布和子环境功能;
  • Solar Nacos SDK 集成灰度蓝绿埋点到 SkyWalking;
  • Solar Nacos SDK 通过 @EnableSolarService @EnableSolarGateway 封装了标准 Spring Boot / Spring Cloud / Apollo / Zuul 等大量注解,降低业务的使用成本;
  • Solar Nacos SDK 和 Solar Eureka SDK 升级和回滚;
  • Solar Nacos SDK 结合 Java Agent 方式,解决异步调用场景下的跨线程调用的上下文丢失。

②  Alibaba Sentinel

  • Solar Sentinel SDK 内置 DEV | FAT | UAT | PROD 四个环境的域名,业务系统无感知
    • SDK通过获取当前机器所在的环境,适配当前所要连接的Sentinel地址
  • Solar Sentinel SDK 深度集成 Apollo SDK
    • Sentinel配置,持久化到服务的apollo的namespace下面,下次重启从apollo获取配置加载到内存
    • 以appId的维度配置应用熔断限流规则
  • Solar Sentinel SDK 整合 OpenTracing & SkyWalking,输出 Sentinel 埋点到 SkyWalking
    • 通过SkyWalking提供的OpenTracing 工具包,手动埋点,获取span信息,推送到SkyWalking持久化到ES
  • Solar Sentinel SDK Dashboard 持久化改造,集成 InfluxDB & Grafana
    • 将Sentinel Metrics数据持久化到时序数据库InfluxDB中,然后通过Grafana 展示
  • Solar Sentinel SDK Limit-App 熔断扩展 (特色功能:灰度蓝绿发布指标的熔断)
    • 灰度蓝绿调用时,如果探测到版本匹配失败时,则抛BlockException
  • Solar Sentinel SDK 网关流控 ,微服务单机限流
    • 全链路压测帮助了解服务能承载流量峰值
    • 信号量隔离/QPS, 并发线程数限流/平均响应时间、秒级异常比率、分钟级异常数熔断
    • 基于 TCP BBR 的系统保护算法,自动探测系统瓶颈,保护系统稳定性
  • Solar Sentinel SDK 集群限流
    • 通过集群限流来解决集群各个机器的流量不均导致整体限流效果不精确的问题

2. 灰度蓝绿和环境隔离

  • 基于 Spring Cloud Alibaba 、Nacos SDK、Nepxion Discovery 开源框架(github.com/Nepxion/Dis…
  • 蓝绿灰度发布 :版本匹配灰度发布、版本权重灰度发布
  • 多区域路由:区域匹配灰度路由、区域权重灰度路由
  • 环境隔离:环境隔离、环境路由

19.png

3. 基于 DevOps 发布平台的智能化和半自动化灰度蓝绿

20.png

21.png 

  • **智能化发布入口界面 **

22.png

  • 蓝绿条件驱动模式界面

23.png

  • 蓝绿权重放量模式界面

24.png 

  • 全量发布和回滚界面

25.png

4. 基于 DevOps 发布平台的滚动无损发布

26.png

  1. 运维 CD 发布平台,先将实例状态设置 disabled ,实例从注册中心拉出;
  2. 消费者订阅注册中心,通知消费者实例处于不可用状态;
  3. 消费者停止路由转发到不可用实例;
  4. 服务上面流量继续处理,30S 后才会启动实例发布脚本;
  5. 实例重启成功后,CD 平台通过请求寄宿在业务 Web 容器里的 VI 接口检测实例健康状态;
  6. 状态检测健康后,注册到 Nacos 注册中心;
  7. 消费者订阅到新的实例,请求正常负载。

5. 分布式追踪和 APM 系统 SkyWalking

  • 所有服务异常监控大盘

27.png 

  • 接口性能指标

28.png

  • 服务维度的异常监控看板,聚合链路中异常数量,帮助业务快速定位问题

29.png 

  • 集成 Solar 全链路灰度蓝绿埋点

30.png

  • 集成 Sentinel 限流降级熔断埋点

31.png

  • 整合服务、实例、接口维度性能指标
  • 快速定位异常、慢 SQL、熔断链路
  • 整合链路与日志
  • 服务、实例、接口、链路维度拓扑图
  • 整合应用诊断系统(Arthas & Bistoury)

6. 应用诊断系统 Arthas & Bistoury

32.png

33.png

  • 无需登录机器
  • 整合 Arthas,Web 化控制台  从 CPU 、线程、内存、JVM 等方面对应用进行诊断
  • 热点方法分析
  • 在线 Debug 

四. Solar 云原生容器化实践

34.png

1. CI/CD 持续发布

  • CD Platform 通过 jinkens 编译打包生成 Jar 包和 Docker img,通过 Harbor 上传镜像到 OSS 平台,Harbor 通过 SLB 做高可用;
  • 自研 Hyperion 中间服务,作为连接 Harbor 和阿里云 K8s 的 API 调用中间层;
  • CD Platform 通过 Hyperion 调用阿里云 K8s API 发布镜像,K8s 从 Harbor 拉取镜像,deploy 到物理节点;
  • 阿里云 K8s 推送状态事件给 Hyperion,Hyperion 将数据推送给 .CD Platform 实时展示发布状态信息。

2. 日志收集

  1. Pod 将日志写到容器内的路径 /opt/logs/{appid}/xxx;
  2. 建立软连接指向 Node 路径 /var/log/{appid}/{podid}/xxx;
  3. 一个物理节点启动一个 FileBeat 进程收集此物理节点上所有 Pod 日志信息;

PS:经过全面的压测,一个物理节点上启动一个 FileBeat 进程收集所有 Pod 日志,性能没有问题 如果存在日志收集不及时问题,则可以一个 Pod 挂载一个 FileBeat 进程来解决,这样的缺点是会占用更多系统资源。

  1. FileBeat 将日志信息推送到 Kafka;
  2. GoHangout 并发消费 Kafka 中数据,持久化到 ES 集群中;

GoHangout 并发消费 Kafka 消息性能比较好。

  1. Kibana 显示 ES 中所有日志索引数据。

3. 微服务弹性扩容和自愈

  • CPU 负载
  • Memory
  • Metrics:根据熔断、限流、监控等组件提供的 Metric 指标做弹性伸缩和自愈;
  • 根据课堂排课信息的容量规划:如下图掌门上课情况非常规律,流量峰值也规律分布,根据排课信息等数据来规划未来几小时内微服务需要承载容量,并按照计划,分配 Pod 数量

35.png

4. 平滑迁移网络解决方案

36.png 

  • 阿里云 Terway Kubernetes 集群提供 CNI Plugin 实现 Pod IP、Node IP 处于同一平面;
  • Pod 每次发布 IP 都会发生变化,内网沿用虚拟机时代的访问和治理方案;
  • 外网访问通过阿里云 SLB,SLB 能自动探测 IP 变化;
  • 基础架构在微服务构建上面已经有很深的积累,充分利用 Spring Cloud Alibaba 以及 Nacos 等一系列服务治理方案 ,使我们云原生容器化过程能够平滑迁移到阿里云 K8s。

结语:以上就是掌门教育围绕 Spring Cloud Alibaba 构建云原生微服务技术体系的一个全貌,掌门教育也会跟随社区技术发展的脚步,朝着微服务 Service Mesh 网格化,serverless 函数计算的方向不断演进,很多年前,大家可能在讨论服务上云的风险,随着云原生技术的不断成熟,现在大家更多的是讨论服务不上云的风险,希望此次分享能给那些准备使用 Spring Cloud Alibaba 微服务系上云的或者正在上云的同学一些实用性的参考和指引,谢谢大家!

Spring Cloud Alibaba 七天训练营火热开营!

PC 端点击链接了解详情:developer.aliyun.com/learning/tr…

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