guys 来聊聊超大型项目重构

921 阅读19分钟

guys 来聊聊大型项目重构

背景

这两年公司业务发展比较快,不管是客户体量、数据量、还是业务复杂度都是爆炸式增长,参与、设计、重构了不少大型项目,包括且不限于CMS 内容平台、营销 SOP、人群分组、会员积分体系、C 端用户体系、平台对象存储标准化等。CMS、人群分组、SOP 又称营销3大件,是营销核心能力,未来准备分享这些业务以及我的思考。感兴趣可以关注我或者帮忙投下票,本文先讲(CMS 重构之路)。

CMS 是什么?

1.0 时代营销主要是派发传单、印制报纸广告、户外大屏投放电视广告力求传播解决物理的隔阂,让跟多的人知道公司的产品;2.0 时代营销更多以流媒体形式、富媒体形式、交互性质的形式…可以通过更多的沟通渠道,短信、彩信、公众号、小程序等触达到客户,2.0时代营销覆盖不是问题,开始谋求效率和收益;个性化和实时性营销帮助企业快速找到企业的客户目标,并且内容正好切中消费者爱好让消费者愿意打开这些内容。

不管是营销1.0还是个性化营销都不离不开『内容』,企业想要客户知道什么?这就是内容(不限于媒体文件、文章、h5等)。所以,CMS 内容平台是最底层和最基础模块,是企业的基座,上层基于 CMS 衍生了一些业务,比如:欢迎语、SOP、公众号群发等。

早期架构

说来也巧,CMS 这块业务第一版我开发过,后来第二版也是我主导重构的,但第二版重构仅重写了代码,代码相对规范了一些,当时自己能力不够,设计也不抽象,仅支撑了1年左右业务增长吧。

早期架构是一个大烟囱,新增功能是一套甚至多套模型,没有复用的概念,如下图:

image.png

CMS 内容平台有十多种素材、还包含媒体文件(图片、视频、音频等)、话术等,衍生了几十种模型,也没有复用组件,面临着几个问题:

  1. 迭代效率低,内容平台有很多共性,新增共性需求所有要改一遍,经常出现漏改、多改问题,研发和测试压力都非常大。也没办法实施敏捷迭代,在业务增长期是很难接受的;

  2. 线上和线下 bug 多,大点的迭代 300 个 bug 常见,甚至有些 bug 研发不知道怎么修复,以及修复产生的影响范围;

  3. 该模块经手研发多,不同研发对业务和模型的理解不同,又没定规范,写代码也是随意;

  4. 代码年久失修,再加上划分聊10+微服务,调用链路复杂,没有人能把代码搞清楚。

以上这些问题不得不劝说产品和老板支持我们重构了。

重构之路

不同业务复杂度不同,有些是业务复杂,有些是数据规模大,CMS 内容平台典型属于业务复杂,更多考虑能力、模型复用。本次重构我们非常兴奋,避免重滔覆辙,做了充分准备,主要做了以下这些事。

参考竞品

行动初期参考竞品,架构该怎么演进以及产品未来走向,我们需要有一个参照物,我们仔细研究了竞品(特赞-创意内容的数字新基建-内容驱动品牌增长)特赞在内容方面做的非常成熟,产品能力丰富,很多功能设计看得出来产研团队是下了功夫的(参考竞品不只是产品的事儿,作为一名研发也需要有产品思维),参考竞品给我们带来了很多好处,比如:

  1. 成熟产品是经过市场验证的,有些 case 可以直接借鉴;

  2. 打开自己思维,站在竞品肩膀上思考更深,产品做的更丰富,提前预留扩展点。

  3. 产品能力越丰富,设计架构看的更远,大部分公司都是产品复杂度和用户规模驱动技术发展的(题外话,我们设计第一版至今没有大动过架构);

当然,并不是所有功能都值得借鉴,基于竞品也做了很多优化。

梳理业务

参考竞品带来了不少灵感,接下来就需要梳理业务,梳理CMS自身业务和依赖该业务业务方。早期业务野蛮生长,扩展了挺多业务,至于这些业务是否有价值,有待考量。

对业务熟悉度一般是测试>研发>产品,我们采取策略是产品只管需要做什么,舍弃没有价值业务;测试梳理全域业务点统一汇总;研发通过一些技术手段比如:血缘关系看板,通过网关流量,构造了血缘关系看板,xx 服务调用 CMS 服务 yy 接口 n 次,类似于这种统计,和测试相互印证功能是否梳理遗漏。另外,测试梳理了近1年线上未解决 bug ,这部分可以作为重点关注。梳理结果如下:

  1. 业务表 50 +,其中统计类业务表有 20 张左右;

  2. 通过血缘关系看板共梳理接口 200+ 微服务 15+(感兴趣可以看看微服务实践的血与泪,从拆分到合并微服务这几年越来越火,大家聊的也比较多,我刚进入初创团队时大家也兴奋不已(也是5年前了) - 掘金);

  3. 涉及业务团队,包含前/后端、测试 12 个,重构基本全部产研团队都要跟着动;

  4. 业务不内聚,CMS 只实现一半能力,另一半接入方实现了;

  5. 线上未解决 bug 100+,有些无法解决,有些需要调整产品;

  6. 统计类业务复杂,前端埋点--->上报后端--->投递kakfa--->报表服务消费--->写入db,报表服务查询展示。早期方案没问题,未来数据体量和功能更复杂,一方面业务很难做,另一方面有些功能是可以相互对比的,数据对不上是大难题,也不能重算。

项目负责人

看吧?梳理下来是有点吓人,这波重构都向你看齐了。对大家要求比较高,尤其是在团队协同和沟通,我们推举了铁三角(后端负责人、前端负责人、测试负责人),主导各自领域系分设计,技术难题攻克;另外还需要一个总负责人,主要是把控全局,遇到无法解决问题向上沟通寻求帮助,也负责架构拆解,冲突解决(这次跟前端冲突非常大,主要体现在接口数据结构约定,一直争吵不休,有些约定是拉着前端总监和后端总监一起约定的)。总负责人跟项目经理并不不冲突,项目经理基本不了解需求和技术,仅跟进项目进度、风险等。铁三角需向总负责人汇报进度、风险。

后端架构演进
定义模型

重新定义了核心模型,不同的素材之间可以共享模型、模型提供的核心能力,下图从烟囱架构演进到共享式架构,文章和表单共用了底层文件夹模型和图文模型,也可以说基于图文模型和文件模型衍生了文章和表单功能,这样做的好处是产品新增需求只关注具体的业务实现,编排各种能力和模型即可。

image.png

数据订正

定义新模型数据订正是不可避免的,数据订正有以下几点需要注意:

  1. 可重复订正;

  2. ETL 工具支持全量和增量数据同步;

  3. 支持按租户订正或时间范围订正;

  4. 需要有日志支持数据是否订正完成;

  5. 无字段变动支持平迁;

  6. ETL 工具最好支持图形化页面,操作简单快捷。

参考下图很清晰,不再赘述。

image.png

数据订正我用过很多方案,如下:

  1. 写 SQL 订正数据,只能处理简单数据或数据体量小的场景;

  2. 跑批,写一个脚本,循环拉取原始数据-->同步目标表,小数据量可行,数据体量大跑不动;

  3. 另外,就是我现在使用方案。

所以,最好的方案还是采集数据库日志来做,这是我最常用的方案,没有一次出过问题,非常爽。

功能内聚

业务方在接入过程中过多识别了CMS 属性,比如:类型、URL链接等,根据不同类型做了不同逻辑,并且这些逻辑是可以收敛的,类型枚举被定义在业务方代码里,扩展业务是非常危险,一不小心就会漏掉。逐步演化了素材包管理组件,集 CMS、活动工具、商品、券等多种能力为一体,还做了生态对接增强,比如适配微信生态和企微、抖音生态数据结构,业务方可开箱即用。

image.png

后来演化成前端提供了通用组件,后端提供了 SDK ,业务接入更方便了。功能内聚优势在于后来的迭代开发更高效,另外bug低。

报表架构简化

报表在初期架构比较简单,前端上报( restful 接口)-->后端应用A(验证)-->kafka-->后端应用B-->DB,后端应用B消费事件做了计算,这条链路前半段没啥问题,问题主要出在应用B消费后计算逻辑。

  1. 逻辑复杂,写 30+ 张统计表;

  2. 稳定性差,遇服务重启一定丢数据;

  3. 准确性差,某张表多加或者少加,用户在业务上是可对比出来的,也没有方案修复,不可能直接改统计。

image.png

最终方案,应用B只写明细表其它交给数据平台,还是老套路业务DB-->ETL-->数据湖,原始数据逐步聚合得到最总结果。报表服务对外暴露接口,简单业务直接对接前端,补数据业务后端中转一次即可。

新方案将复杂逻辑下沉至数据平台,大家可以自行思考利弊。

原子接口

接口设计是最差的一个模块,接口按业务功能实现,每个功能一套 CURD,导致后来迭代接口膨胀到 120+,好在接口层只是组装了下层能力,调整起来也比较方便,但是影响范围巨广。近期计划联合前端重构接口,因为一些原因搁置了。可以参考下优化思路,因为数据结构差异不大,可以用一套 CURD 收敛,对于一些细微差异用策略模式优化,其它就是工作量问题,并不复杂。

缓存方案

缓存问题是被大家讨论非常多话题,我自己看了不止10篇文章吧,大多都是大差不差的。可能到现在有人还纠结先删除缓存再删除数据库?还是直接上一个高级的,通过投递消息队列,通过消费消息队列消息删除缓存,如果你还纠结那恭喜你,看了之后你应该不会纠结了。CMS 缓存删除机制一直被诟病,先看看方案。

image.png

不出大家意外,我们选了最复杂方案,该方案有几个缺点:

  1. 链路长复杂,任何一条链路出问题,都会导致你排查半天,对新同学也不友好;

  2. ETL 工具是基建团队开发的,工具在持续升级,升级了几个大版本最后只能业务方适配,跟着调了不少次;

  3. 数据库集群 binlog/oplog 日志巨多,ETL 工具采不过来,缓存删除延迟,出现不一致了。

你说坑不坑?缓存是针对读多写少的场景,如果数据变化快不适合用缓存。一般先更新数据库,再删除缓存+缓存设置过期时间足够了,我们有些场景缓存每日读取 3000w+ 也没出现过问题,架构一定是简单优于复杂。

发布计划以及评审

发布环节往往最容易被大家忽略,但是出问题就是大的灾难,一整天发上不去最后只能以发布失败告终。我们也遇到过这类情况的,最后复盘发现问题非常低级,常见的是配置问题、代码合并问题、沟通不到位(到发布阶段了还有同学不知道要发布)、脚本未整理、功能遗漏等。所以项目发布前一定要拉上所有相关人员确定整个发布流程,具体见下面这张图:

image.png

另外补充一点,一定要养成随时记录和整理文档习惯,对于迭代周期长版本不及时记录很容易忘记。

项目复盘

项目上线后,建议大家总结项目过程中的得与失,哪些是做的不好的,是否有更好的方案,把整个过程回顾、分析、讨论。一次项目迭代过程,肯定会有做的好的地方,也会犯一些错误,复盘就是要分辨出最佳案例,继续保持;做的不够好的,找出原因,针对性改进,避免再犯同样的错误。

复盘很多团队做不好也包括我,搞不好大家鸡飞狗跳相互甩锅,整个会议就是乌烟瘴气,会议结束貌似没啥效果,失去了复盘意义,复盘应该注重以下几点。

  1. 目标回顾:每个项目启动时都会设定一些目标,在会议开始前先回忆目标,让大心中有数方便评估最终结果。目标设定要可达、客观,比如:CMS 项目初期设定目标是减少业务表 30 +、微服务下线5个、1个半月完成开发和上线等。目标要竟可能准确和可评估;

  2. 评估结果:项目结果评估要分 2 类,好的情况和坏的情况,比如:CMS 未按照原计划上线,整个里程碑推迟了半个月,但是上线后解决了线上 BUG 100+,下线业务表 30 +,整个产品体验提升了一大截,鼓励大家在开发过程中积极记录,勇于提出来;

a)  坏的结果:产品延期,没有按照既定的里程碑交付;

b)  好的结果:解决了线上 BUG,稳定性提升了,架构复杂度降低了,客户体验感提升了。

3、  原因分析:结果评估以后,就要分析原因了,什么导致了坏的结果,哪些原因导致了好的结果;

a)  坏的结果原因:CMS 属于底层模块范围广,技术评估遗漏中途需求不断扩散导致一直在调整;

b)  好的结果原因:本次看考了竞品,借鉴了不少成熟设计思路产品体验得到提升,另外做了一些抽象能力,这些能力帮助产品提升了的稳定性。

4. 总结方案,做出改变:前 3 步还不够,分析完原因后需要归纳方案,在未来的项目中做出有效方案,帮助大家在以后的项目中做的更好。对于好的实践,继续保持;对于不好的实践,停止并寻求改变。

如果项目周期是以季度进行的并且上线后效果还不错,建议老板们在项目上线后给大家一些奖励,比如:办公用品、奖杯、现金奖励等,毕竟一个项目持续一个季度+,大家是付出了很大努力的。

踩坑&总结&改善

大型项目开发肯定会踩特别多坑,CMS 项目也不例外,把它们单独拎出来,大家可以看看热闹。

  1. 第一次发现前后端协同如此困难,首先是数据结构定义上,前端期望按照页面样式返回数据,有些场景要求返回更多数据;

    a. 接口字段大而全,不具备通用型还比较定制;

    b. 过度查询数据,接口性能差。

    争吵了很多次,遇到不合理扯皮又扯不过找领导是最好方式(遇事不决找上级),前提是有一个好领导很重要。

  2. 不同数据库集群关联查询,业务数据在 TIDB 统计数据在 Doris,有些场景需要支持业务数据和报表数据联合查询(排序场景), TIDB + Doris 关联查询上线后性能差到爆,最后将 Doris 数据回流至 TIDB 问题解决了;

  3. 统计类业务部分场景通过 SQL 从明细表聚合查询,明细表数据体量大性能差,把明细数据清洗成按天、按内容ID等维度数据,提前预聚合提升查询性能;

  4. 项目迭代周期1个季度,大量数据订正、涉及 10+团队,升级应用70+,首次上线失败,发布成本太高(沟通、协同、配置、bug 等每个链路都遇到问题),项目开发初并未考虑向下兼容业务方逐步接入,主要是向下兼容成本高。后来开发大型项目采取了2种方案:

    a. 开发新功能,引导客户使用新功能,下线历史功能(开发成本高场景适用);

    b. 向下兼容,兼容历史接口/模型,开发方优先上线,业务方逐步接入。

    上面2种方案有效解决了我们大版本发布遇到的问题,但是周期拖特别长。

  5. 接口设计烂,一个功能一套 CURD 接口,比如:表单一套、文章一套、A一套等,细数下来 100+,很多接口是可以合并的接口逻辑大差不差。后来迭代种发现新加一个字段改造接口多,前后端开发都费劲。主要原因是项目大精力分散,又安排新入职同学设计和开发,也没有详细 review 造成了这个问题。后来,接口设计成了我们系分设计必不可少模块。

上述问题比较典型,也是冰山一角,大家在开发过程中要善于发现和总结问题,吸取经验未来做的更好。

业务结果

项目上线后拿到了不错结果,包括不限于下面这样图,另外,还增加了业务监控、告警,稳定性也得到了很大提升。

image.png

总结

好了,整个重构过程梳理完成了,看似简单但重构过程非常艰辛,开发到上线一群人用了整整3个月,当然这个过程中我们也吸取了很多教训,后来很多大型项目基于这次的经验,开发并且上线了,未来有机会再聊。大型项目重构有几点比较重要。

  1. 一定要参考竞品,借鉴好的设计思路,帮助你们快速构建产品;另外在架构设计上看的更远,有利于驱动技术发展;

  2. 推举铁三角(前/后端、测试、产品),铁三角提前沟通需求、调研技术方案可行性、以及需求问题,减少需求评审时间,提高效率。另外,还需要一名总负责人,一般是团队TL,主要负责技术拆解、进度管理、跨团队沟通协调,技术问题攻克以及向上管理;

  3. 系分设计,将架构逐步拆解和细化,细化到能指导研发开发;

  4. 提前指定发布负责人,由发布负责人整理文档、拉评审会议、沟通和协同兄弟团队、灰度发布以及全量,发布负责人可以是铁三角中的任意一个;

  5. 4步做好项目复盘,回顾目标、评估结果、分析原因、总结方案,做出改变,条件允许复盘后对做的好的 case 申请一些奖励鼓励大家继续努力。

2024 年最后一篇,觉得有用请点赞、关注。2025 年再见,祝安康。

最后,大家帮忙投个票吧,谢谢。

image.png