腾讯文档收集表后台重构:改造一个巨石单体!

企业   2024-05-06 17:07   广东  


收集表是腾讯文档的核心品类之一,也是主要的用户增长来源渠道。作为在重大社会事件中承担社会责任的主要功能,收集表既面临着海量规模的压力考验,也在高速发展的业务进程中遇到了遗留技术债的掣肘。

- 核心服务为C++“翻译”过来的 C++ 风格单体非标 tRPC-Go 服务,代码量较大,不利于多人敏捷协作开发,业务快速迭代时期夹带发布风险高,故障爆炸半径大。
- 业务逻辑耦合严重,接口未做轻重分离,稳定性较差,性能存在瓶颈。
- 业务可观测性存在问题。

在这样的技术背景下,腾讯文档团队对收集表后台服务进行了全面的重构,实现了百万级大收集极限业务场景下提供稳定解决方案的业务收益,完善了底层技术基座,优化了产品体验,实现了开着飞机换引擎的重构效果。





01



业务介绍

腾讯文档作为用户最广泛使用的在线文档,支持 Word、Excel、收集表、PPT、思维导图、脑图/流程图等品类多人协同在线编辑。近两年用户规模上不断突破,保持行业领先,也取得了不错的社会效应和用户认可。

收集表作为腾讯文档核心品类之一,在特殊时间成为文档最主要的用户增长方式,持续发挥信息传递的价值,在各类重大社会事项场景中及时响应推出解决方案,让“救命文档”的标签进一步深化。此外,团队不断提升底层技术能力,支持单收集表百万提交和数据导出与同步。


但另一方面,收集表的后台服务可用性存在一定的历史包袱,接口成功率稳定性差,用户负面反馈时有发生,一定程度制约了业务的良性发展。




02



技术背景

   2.1 老单体服务


  1. 核心服务是一个由 C++“翻译”过来的 C++ 风格单体非标 tRPC-Go 服务,代码量较大,不利于多人敏捷协作开发,业务快速迭代时期夹带发布风险高,故障爆炸半径大。
  2. 代码规范度差,不符合公司 Go 代码规范,由一个庞大的指针对象全局传递,不符合高内聚、低耦合的设计原则,导致难以观测系统运行时上下文状态。

   2.2 稳定性差


  1. 业务逻辑耦合严重,接口未做轻重分离,边缘业务接口影响主链路失败率,无法提高核心接口成功率和被调耗时。
  2. 核心接口高耗时,导致热收集表并发提交场景存在较大性能瓶颈。

   2.3 协议未约束


  1. 由前端定义 json 结构构造请求入参,后台未约束为强约束型 pb 结构,导致缺乏数据校验。
  2. 前端请求脏数据拉低后台接口平均成功率,后台无法清楚感知数据变更,导致成功率常年不达2个9。
  3. 原 formcollect 服务接口和一些数据结构被设计为嵌套解析。各级字段以 json 字符串存储,访问时需要层层解析,修改底层字段值后还需要层层编码。对核心数据结构的编解码代码分散在服务各处,一些数据预处理和修复逻辑也多处复制粘贴。


   2.4 存储混乱


  1. 生产和测试存储混用,不同业务之间存储混用,导致彼此相互影响,一损俱损。
  2. 对于前端请求数据无脑落盘,导致存储中脏数据严重。

   2.5 业务可观测性差


  1. 缺乏业务监控和日志上报,未从可观测性角度去观测系统局部以统筹整体。
  2. 无效告警多,且由于不正规处理方式,导致无法区分业务逻辑是否正常,缺乏人力跟进处理。
  3. 用户反馈频繁,但有效处理极少,常年稳居头部反馈。


因此,从2022年7月起的近1年内,新增需求如周期收集、新名单、导出归档等开始,我们针对收集表后台的新服务进行标准化开发和老服务重构优化。在保证新服务可用性、稳定性、可观测性都达标的前提下,逐步对存量巨石服务进行拆分重构。重点保障:
  1. 核心链路轻重分离,非核心链路异步化拆分,降低主接口耗时。
  2. 松耦合设计,保证所有后台接口柔性可用。
  3. 严格遵循公司代码规范,模块内部高内聚,模块之间低耦合。
  4. 执行分级CR制度,先模块同事相互 CR 后,再交由 CR 骨干/专家进行二次 Review。
  5. 生产测试存储隔离,提高现网存储稳定性。



03



架构设计

   3.1 后台架构



   3.2 设计原则


   3.2.1 研发规范


为加快业务迭代效率和代码可读性,针对老单体非标服务进行了拆分重构,采用大仓通用脚手架生成服务,业务垂直分层为 service logic repo 等,不同分层和函数之间低耦合,模块内部业务逻辑高内聚。

   3.2.2 轻重分离


为提高核心接口可用性,我们采取软件工程经典“分治”思想,进行了逻辑和架构分层治理,拆分出读写原子服务,由业务逻辑层调用原子接口;同时,将核心链路中的可异步化逻辑剥离,由消息队列解耦后异步处理,如提交同步表格、发布作业重判等。不仅优化了主链路高耗时问题,也保障了核心链路(提交、发布、查询)接口成功率。


   3.2.3 柔性可用


通过有损服务给用户提供柔性降级体验,例如在提交数据同步表格过程中,当提交数过大时,同步至表格侧的数据会发生写放大,单收集表 1w 提交数据在表格侧可能占用数十万甚至上百万单元格,对下游表格中台/编辑中台的压力较大。

因此我们设置了表格侧同步上限,触及同步上限的内容将不再继续增量同步,引导用户通过终端导出本地后查看,既对下游产生保护作用,同时也降级保障了用户的使用体验。

   3.2.4 松耦合架构


架构范式从过去的紧耦合过渡为松耦合,按照业务模块分为通用逻辑、导出归档、新名单、周期收集、同步表格等19个服务,各个模块可以独立开发、部署、扩展,使业务支持快速迭代,减少部署发布带来的变更风险和心智负担。组织和团队结构可以围绕业务服务去构建,专注于当前业务模块,从而灵活根据业务需求来使用人力,对于协作效率的提升和交付时间的缩短都有重要作用。

   3.2.5 存储隔离


  1. 按照业务模块进行垂直拆分 DB,实现物理隔离。
  2. 按照用户环境进行拆分 DB,生产与测试环境物理隔离,避免相互影响。
  3. DB 和缓存、中间件由单分区和标准架构升级多分区集群架构,主从副本分散到不同可用区。
  4. 按照收集表数量水平分成10个 DB,按收集表 ID 一致性 Hash 到不同 DB 的表。
  5. 按照收集表 ID 一致性 Hash 到单 DB 的不同分片。

   3.2.6 高度可观测


系统可观测性作为业务最后一道生命线和云原生成熟的重要衡量标准,为我们观测系统运行时状态提供工具、方法论、基础设施等一系列指导,可观测性驱动开发(ODD,Observability Drive Development)这一研发模式也正在逐步成为云原生分布式系统中最佳实践。

腾讯也在可观测领域持续打磨和沉淀,有如:腾讯云可观测平台、天机阁等成熟的观测产品。腾讯云可观测平台既服务于外部公有云用户,同时也会服务于腾讯集团的内部用户,天机阁目前只服务于腾讯集团内部客户。

腾讯文档在业务实践中大量使用天机阁可观测性平台,在腾讯文档 C 端、企微 Saas 版本、企微私有化、腾讯文档独立私有化等版本中都几乎全量上报天机阁,包括 Traces、Metrics、Logs 等,原始上报量 700+TB/d。收集表也是天机阁重度使用品类,通过 ODD 帮助研发、测试、产品、运营同学通过数据理解业务,达到一切尽在掌握的目的。以下将进行详细说明。



04



业务模块

   4.1 数据同步在线表格


收集表数据同步表格是典型的业务最终一致性场景,其中需要考虑同步速度,数据是否最终一致等问题。在业务实践过程中,经历了以下3个阶段:
  1. 用户量少,收集表少场景:业务处于发展前期,此时1.0链路同步写数据到表格即可满足业务基本诉求。
  2. 用户量上涨,单收集表提交量上涨:因为下游接口耗时提高或偶现抖动,导致同步写数据容易造成数据丢失,且单纯重试无法解决。此时1.0链路无法满足业务诉求,启动对同步写操作解耦,通过消息队列将数据交由下游消费者处理,消费者失败重试阻塞消费。
  3. 用户量猛增,单收集表提交量上百万:由于需要保证数据同步顺序与提交顺序一致,单收集表提交数据需经过一致性 Hash 生产到 Kafka 对应 Partition。但因为上游脏数据和下游耗时波动等原因,在热收集表场景 Kakfa 容易消费失败导致 Kafka 发生频繁 Rebalance,从而造成 Partition 内消息积压严重。

极端情况下,热收集表导致有过半 Partition 都积压超过 1w 条数据,也会影响到相同 Partition 内其他收集表的正常同步,需要人工介入手动停止热收集表同步,普通收集表才会恢复。

于是选择将 Kafka 队列做冷热隔离,热收集表的同步数据降级生产到单独热队列,普通小收集表同步数据生产到普通队列。这样可以优先保证现网95%收集表的同步正常,但对热收集表的同步有损较为严重。


同步慢原因
  1. 品类之间数据模型是异构的,收集表和表格侧的读写性能不同,大表格写的慢,且逐条同步有锁导致处理效率不高。
  2. 单篇大收集表数据同步消费慢,会阻塞分片队列里其他的文档数据同步,导致其他普通收集表也延迟同步。

同步失败原因
  1. 没有记录流水表,没有记录逐条数据的同步中间状态,简单的立即重试失败后即丢弃,再也没有重试机会。
  2. 可能导致失败的原因:下游表格中台有单机限流、单文档限流;oidbpad 也有单机处理队列限制,当负载较高时,都有可能处理失败。


经调研,热收集表用户通常是高商业化价值人群,如何服务好这类用户对于支撑业务快速发展和产品口碑建设更是至关重要。于是我们针对以上问题启动了新一轮同步重构。

经过仔细复盘分析,热收集表的管理员,对于数据同步实时性要求不敏感,几 w 甚至几十 w 提交的热收集表数据同步有少量延时是完全可以容忍,从宏观上难以洞察局部同步延时。最终决定将数据同步由消息队列异步化重构为流水表中间件记录状态,再由定时任务和消息通知去处理执行,以时间换效率。


2023H1,腾讯文档开始灰度新建 sheet 3.0 并全量现网,收集表关联表格也需要完成同步逻辑切换,针对存量 sheet2.0,smartsheet2.0 保证继续同步表格中台,针对新建关联 sheet3.0,smartsheet3.0 需实现编辑中台同步逻辑。

在新同步链路框架中,上游同步控制服务无需关注具体同步细节,只负责全量任务的发起和增量数据的执行;formtosheet 服务会统一对接下游多个新老数据同步方。服务内部高度内聚,做好自己的工作,同步系统稳定性得到大幅提升。


新同步框架上线前,我们也做了完备的性能压测,在新增 5k 和新增5k+10%修改情况下,模拟对比2.0和3.0同步链路在并发提交场景的性能差异,结果如下,新增同步速度从原来的分钟级提升到秒级,即使在10%修改情况也可以保证秒级同步完成。

   4.2 新名单重构与实现


收集表支持设置填写成员名单,用于管理填写者信息和行为。老架构只支持通用名单设置,且通用名单实现上比较简单,仅通过 Redis 存储所有名单信息,此时单 Redis 集群会成为系统瓶颈所在,且 Redis 一旦因为故障重启容易导致全量数据丢失。

因为居家期间大量信息收集和学生居家上课作业收集等需求日益增多,产品侧启动对家校名单需求的开发,后台侧也趁机针对原有不稳定架构进行重新设计与实现,重点保障名单数据长期安全存储。


老名单链路通用名单只存储 Redis,在家校名单场景现有技术架构也无法满足产品需求;家长可以帮孩子填写对应收集表,且在多孩多家长场景下,孩子与家长并非简单一对一关系,一个孩子可能有多个家长,一个家长也可能有多个孩子。此时针对学生的信息填写需要进行严格的身份绑定和校验,针对不允许重复填写时需要以学生角度校验是否已被其他家长填写,这其实类似于复杂版本的群组概念和社交关系链。难点转向了业务逻辑复杂度和缓存存储的数据一致性。

  1. 在复杂业务逻辑情况下,良好的架构分层和模块高内聚可以有效降低业务逻辑复杂度,如果后续增加名单类型如企业名单等,可以将开发范围控制在基础管理的增删改查和身份校验模块,对于关联关系和绑定不需涉及业务逻辑改动,有效降低对系统的理解成本。
  2. 对于缓存实效瞬间导致的大量请求打到存储,容易引起存储瞬间高负载情况进而导致缓存击穿,我们在收集表名单模块和基础读链路也引入了 singleflight 组件,阻塞大量重复读请求,将同时刻的并发请求聚合为单个下游请求,在单收集表高并发打开提交场景对后端存储起到很好保护作用。


   4.3 导出归档


收集表顾名思义重在收集,和在线文档、在线表格这类更偏向协作的品类不同,收集表是比较典型的 UGC 场景,用户收集完成表单内容后需要进行数据的整理汇总,将表格和附件导出本地能够很好的协助用户做数据归档,支持自定义附件导出归档文件夹路径,按照单题维度导出等。

导出场景是典型异步任务场景,后台数据需经过导入导出侧格式转换、分类归档、压缩打包后,交由端侧完成下载。在2022.10最初版本导本地时,统一由收集表后台提供用户数据,导入导出侧下载到服务器后完成按规则压缩打包。当附件总大小超过 500MB 时,会分为多个压缩包给到用户,这就带来如下问题:

  1. 当遇到热点收集表,如挑战杯信息统计、信息截图收集等可能过百万提交场景,单收集表超大容量会给下游导入导出侧带来较大挑战,会因文件总量过大导致下游服务偶现 OOM。
  2. 分压缩包逻辑可能导致单个用户的多个提交附件被分到多个压缩包内,时常有用户反馈导出本地后有丢数据,实际并没有数据丢失,而是因为某些用户在当前压缩包的内容不完整而被分到其他压缩包中造成的错觉,属于用户痒点。


因此产品规划2023年将收集表导出功能迁移至由桌面端承接,总量 <20MB 的附件仍由发起导出的端侧进行下载,而超限附件将拉起桌面端完成。桌面端导出时跳过导入导出较重的下载打包逻辑,将所有附件列表返回给桌面端,由桌面端按文件列表和导出规则依次下载,不仅以较低成本实现了断点续传,同时可以很好的保护下游导入导出服务防止频繁内存溢出。




05



服务质量

   5.1 可观测性


可观测性作为现代云原生应用的生命线,通过多维数据模型高度保障线上业务平稳运行可观测。腾讯文档全面接入天机阁可观测性平台,收集表又是示范接入品类和 ODD 重度依赖方。收集表后台新服务全量采用 ODD 可观测性驱动开发模式,从持续规划—>持续构建—>持续交付—>持续运维,覆盖产品研发运维 DevOps 的全生命周期,辐射前端、终端、后台、产品等多种参与角色。

但是目前天机阁不对外服务,大家有企业可观测性建设的需求,可以试用腾讯云可观测平台(TCOP):TCOP包含了云拨测 (CAT),云压测(PTS)、前端/终端性能监控 (RUM)、应用性能监控 (APM)、Prometheus & Grafana 服务以及云产品基础监控等多个子产品,经过往几年产品的打磨与积累,在可观测领域已经形成了相对比较完整的产品矩阵,且积累了丰富的行业客户案例。新客户也有免费 15 天试用期。

体验地址:https://cloud.tencent.com/product/tcop


   5.2 持续规划


通过重点理解产品业务需求,了解产品重点关注的除服务基础容器监控、主被调监控、请求链路等之外的核心业务指标,如对于收集表导出本地场景,我们意识到用户导出 Excel 和附件的导出行为占比与导出附件数量分布等指标对于产品未来入口设置和产品功能决策有较大意义。这驱使我们更关注业务对于导出数量上限的要求和能力,提前做好对应兜底方案。

   5.3 持续构建


在研发过程中,利用 tRPC 提供的标准 tRPC-Metrics 和社区 Prometheus 原生  API 做好对应指标监控项的初始化;通过面向接口编程的依赖注入,以更优雅方式实现上下文信息透传,将辅助判断用户行为的 tag/attribute 提前设置到分布式链路追踪里,待有告警或用户反馈时有更多信息辅助判断,缩短故障恢复时间。


   5.4 持续交付


传统数据上报和数据分析存在时延性较高、无法动态调整上报策略等弊端,而通常产品上线前期的实时数据有更大的分析意义,有助于上线后根据实时数据调整产品逻辑。

如导出归档功能上线后,我们观测到用户导出 Excel 和附件分别占比70%和30%,说明用户对附件导出需求较大,便于产品提前规划后续针对附件导出场景优化;用户导出全量数据和单题占比分别为90%和10%,根据数据可以反哺产品后续对导出按钮的曝光设置等。



   5.5 持续运维


对于平稳运行的业务,后台日常监控告警和服务质量优化需要保持长期关注。因此我们配置了天机阁/伽利略等多个告警通知渠道,通过企微群、电话、邮件等方式全面监控服务异常波动。


针对后台质量做长期存储和 SLA 汇总,也制作了相关日报业务看板,由于实时查询 Prometheus 拉前一天的数据,会将整个租户数据全部加载到内存中,查询过缓慢导致 Grafana 加载失败(实测 30s 以上),且极端情况数据过多可能导致 Prometheus 单节点 OOM。

每天凌晨定时从 Prometheus 通过原生 SDK 抓取数据写入到 ClickHouse,通过物化视图实现毫秒级日报数据查询,优势在于,可用 Map 类型,对于报表方便增加字段不需要修改表结构;自带 TTL 功能,当前默认1年。表引擎选择 Distributed,因为买的 ClickHouse 是大存储型,默认没有副本冗余。所以设置每天定时写多份,在查询时做 distinct 去重。


目前该功能已有收集表、账号、权限、文档数据、导入导出等模块启用。方便推动自身及合作方及时进行业务整改和质量优化,同时也在腾讯文档私有化版本中作为前端和后台 SLA 质量数据发挥作用。

   5.6 持续性能分析


持续性能分析,即 Continuous Profiling, 是一种强大的技术, 是更强大的 Profiling 版本,增加了时间维度. 可以从任何环境(包括生产环境)连续收集行级分析性能数据。然后它提供数据可视化,以便开发人员可以分析、排除故障和优化代码。

天机阁持续性能分析系统支持持续的性能分析,并将数据保存在服务端,以供 Profiling UI 近实时的查看、回溯、分析。


使用持续性能分析系统的收益:
  1. 减少分析和故障排查的工作量。持续性能分析使不同应用程序版本和环境的性能比较变得容易。它可以减少发现性能瓶颈(包括微小瓶颈)所需的工作量,最终增加持续性能改进的可能性。
  2. 降低成本最显着的好处是服务器资源利用率的显著降低。此外,考虑到云环境会给大型组织带来多少成本,代码分析将直接转化为降低运营成本。得益于持续性能分析最大限度地减少了开发人员执行性能任务所花费的时间,基础设施成本变得更容易实现。
  3. 可扩展性和可靠性。由于不断消除性能瓶颈,整体可扩展性也可以显着提高。此外,由于瓶颈通常是资源过载等生产事故的原因,因此还可以通过显着减少瓶颈来提高可靠性。
  4. 从生产环境中的不良性能中恢复。当新的部署被引入生产时,并不总是双赢的局面。有时,指标在生产中表现不佳。不过可以回滚到最后一个已知的稳定部署, 并对在预生产环境中重现的问题进行根本原因分析。代码分析可以帮助查明预生产环境中的问题。但是,在生产环境中,负载等不可预测的方面仍然难以复制。持续分析使用来自当前性能不佳的系统(回滚或发布之前)的信息解决了这一挑战。它将先前 Profiling 中的数据关联起来,并快速确定性能不佳的原因。

   5.7 智能告警


随着云原生可观测性行业蓬勃发展,如何利用可观测性数据进行更高维度分析和预测成为下一个核心命题。在参与中国信通院《云上稳定性·根因分析行业标准》制定过程中发现,利用可观测性数据实现系统根因分析是国内外厂商正在聚焦的重点。天机阁5月新灰度智能告警功能,腾讯文档租户第一时间启用并推送至告警群,很快就在业务线上故障排查过程中发挥重要作用。经实践验证,对缩短 MTTR(平均故障恢复时间)有非常明显效果。以下举例说明。


5.22日收集表下游 sheetengine 存储高负载导致瞬间产生大量慢查询超时,引起收集表后台异常率上升。14:56收到群内告警推送和电话告警,迅速到天机阁智能告警群寻找关联信息,发现下游 sheetengine 连接打满导致的超时,立刻联系对应同学处理。

收到故障发生告警 —> 定位问题所处环节 —> 同步相关责任同学,一共耗时在 1min 内,相比之前可观测性数据排查需由告警—>监控—>具体 Trace—>关联日志,链路较长导致定位问题耗时较长;智能告警可以将可观测性数据上下文打通,把相关信息直观呈现给开发同学,更快速高效定位问题,以更短的 MTTR 恢复用户体验。

   5.8 持续反馈


对于兔小槽用户渠道反馈,我们也做了完备的数据收集和实时推送,随时掌握一手信息并提前跟进排查,对于咨询类反馈,推动运营同学联系用户协助处理; 对于bug类反馈,拉通前端同学共同跟进解决。秉持以用户价值为依归的责任,做到故障反馈“0”容忍





06



业务收益

经过为期半年多的重构,我们解决了收集表长期可用性较低的问题,有效支撑业务在快速增长期稳定运营,在百万级大收集极限业务场景下提供了稳定解决方案,为产品赢得良好用户口碑。
最后也感谢收集表所有前端、后台、产品和运营同学对相关工作的鼎力支持与通力合作!

-End-
原创作者|张瀚元

活动介绍:GIAC全球互联网架构大会于5/24~25日在深圳举行,大会中有更多来自微服务、AI及大模型精彩案例,点击“阅读原文”了解更多议题。


参考阅读:



本文由高可用架构转载。技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿


高可用架构
高可用架构公众号。
 最新文章