阿里数据中台核心产品揭秘

2021年10月14日12:33:03 科技 1803

编者按:作为阿里数据中台的核心产品,Quick BI 单一代码仓库源码已经突破了 100万行,正在向1000万行迈进。本文重点分享了单一代码仓库Monorepo选用前的思考,以及具体应用中的开发体验和经验。内容转载自“Alibaba F2E”。

近年来,阿里数据中台产品发展迅速。核心产品之 Quick BI 连续 2 年成为国内唯一入选 Gartner 魔力象限的国产 BI。Quick BI 单一代码仓库源码突破了 100万行。整个开发过程涉及到的人员和模块都很多,因为下文讲的一些原则,产品能一直保持在快速的开发状态。

先分享几个关键数据:

代码:TypeScript 82万行,样式 Sass+Less+CSS 18万行。(cloc 统计,去除自动生成代码)

协同:Code Review 12,111 次,Commit 53,026 次。

阿里数据中台核心产品揭秘 - 天天要闻

很多人会问,这么多代码,为什么不切分代码库?还不赶快引入微前端、Serverless 框架?你们就不担心无法维护,启动龟速吗?

实际情况是,从第一天开始,就预估到会有这么大的代码量。启动时间也从最初的几秒钟到后面越来越慢5~10分钟,再优化到近期的5秒钟。整个过程下来,团队更感受到 Monorepo(单一代码仓库)的优势。

这个实践想说明:

大的 Codebase 可能是好事情,大道至简。用极其“简单”的架构更容易支持复杂灵活的业务

要做到简单的架构,内部需要更明确的规范,更密切的协同,更高效的执行

能通过工程化解决的问题,就不要通过开发规范,能通过规范来解决的不要靠自由发挥

开工

2019年4月30号,晴朗的下午,刚好是喜迎五一的前一天,发挥集体智慧,投票选出满意的仓库名。同时借 Quick BI 和 FBI 底座融合的契机,项目开启。后来底座代码转正,把上层业务代码也吸纳进来。

commit 769bf68c1740631b39dca6931a19a5e1692be48d
Date:   Tue Apr 30 17:48:52 2019 +0800

    A New Era of BI Begins

Why Monorepo?

阿里数据中台核心产品揭秘 - 天天要闻

在开工之前,对单一仓库(Monorepo)和多仓库(Polyrepo)团队内做了很多的讨论。

曾经我也很喜欢 Polyrepo,为每个组件建立独立 repo 独立 npm,比如2019年前,单是表单类的编辑器组件就有 43 个:

阿里数据中台核心产品揭秘 - 天天要闻

本以为这样可以做到完美的解耦、极致的复用,但实际上:

每次 Babel、React 等依赖整体升级能让人脱层皮,所以自研了脚手架。造轮子都是被逼出来的,事情做了一点点,但写脚本能力直线上升。

每次 调试组件,npm link 一下。后来组件跨级,可以做 3 层 npm link,使用过的都知道这是多么糟糕的体验。

版本难对齐,每次主仓库发布前,组件间版本对齐更是考验眼力,稍有不慎触发线上故障。

方便别人复用的优势呢?最终支持自己业务都捉襟见肘,哪还敢让别人复用……

最终我们把所有这些组件都合并到一个仓库,其实像 Google/Facebook/Microsoft 这些公司内部都很推崇 Monorepo。

但我们不是原教旨主义的 Monorepo,没必要把不相关的产品代码硬放到一起。在实线团队内部,单个产品可以使用 Monorepo,会极大降低协同成本。但开始的时候,团队内还是有很多疑问。

关于 Monorepo 的几个核心疑问

**单一仓库,体积会很大吧?**

100 万行代码 的体积有多大?

先来猜一下:1GB?10GB?还是更多?

首先,按照公式计算一下:

代码的体积 = 源码的体积 + .Git 的体积 + 资源文件(音视频、图片、其他文件)

1. 我们一起来算一下源码的体积:

一般建议每行小于 120 字符,我们取每行 100 个字符来算,100 万行就是:

100 * 1000,000 = 100,000,000 B
转换之后就是 100 MB!

那我们的仓库实际多大呢?

只有 85 MB!也就是平均每行 85 个字符。

2. 再来算一下 .git的体积:

.git里记录了所有代码的提交历史、branch 和 tag 信息。会很大体积吧?

实际上 Git 底层做了很多的优化:1. 所有 branch 和 tag 都是引用;2. 对变更是增量存储;3. 变更对象存储的时候会使用 zlib 压缩。(对于重复出现的样板代码只会存储一次,对于规范化的代码压缩比例极高)。

按照我们的经验,.git记录 10,000 次 commit 提交只需要额外的 1~3 个代码体积即可。

3. 资源文件大小

Git 做了很多针对源码的优化,但视频和音频这类资源文件除外。我们最近使用 BFG 把另一个产品的仓库从 22GB 优化到 200MB,降低 99%!而且优化后代码的提交历史和分支都得到了保留(因为 BFG 会编辑 Git 提交记录,部分 commit id 会变化)。

以前 22 GB 是因为仓库里存放视频、发布的 build 文件和 sourcemap 文件,这些都不应该放到源码仓库。

小结一下,百万行代码体积一般在 200MB ~ 400MB 之间。那来估算下 1000 万行代码占用体积是多少?

乘以十也就是 2GB ~ 4GB 之间。这对比 node_modules随随便便几个 G 来说,并不算什么,很容易管理。补充个案例,Linux 内核有 2800 万行,使用 Monorepo,数千人协同。据说当时 Linus 就是为了管理 Linux 的源码而开发出 Git。

**启动很慢吧?5分钟还是10分钟?**

听到有些团队讲,代码十几万行,启动 10+分钟,典型的“巨石”项目,已经很难维护了。赶紧拆包、或者改微前端。可能团队才 3 个人却拆了 5 个项目,协同起来非常麻烦。

我们做法有3个:

按照页面来拆分多 Entry,每次只需启动一个 Entry

梳理子包间的依赖关系,追求极致的 Lazy loading,Tree-Shaking

Webpack 切换到 Vite

尤其是 Webpack 切换到 Vite 以后,最终项目冷启动时间由 2-5分钟 优化到 5秒 内。热编译时间由原来 5秒 优化到 1秒 内,Apple M1 电脑基本都是 500ms 以内。

**代码复用怎么办?Monorepo 复用的时候是否要引入全部?**

传统的软件工程思想追求 DRY,但并不是越 DRY 越好。

每写一行代码,都产生了相应代价:维护的成本。为了减少代码,我们有了可复用的模块。但是代码复用有一个问题:当你以后想要修改的时候它就会成为一个障碍。

对于像 Quick BI 这样长期迭代的产品,绝大部分需求都是对原有功能的扩展,所以写出易维护的代码最重要。因此,团队不鼓励使用 magic 的特技写法;不单纯追求代码复用率,而是追求更易于修改;鼓励在未来模块下线的时候易于删除的编码方式。

对于确实存在复用的场景,我们做了拆包。Monorepo 内部我们拆了多个 package(后面有截图),比如其他产品需要 BI 搭建,可以复用 @alife/bi-designer,并借助于 Tree-Shaking 做到依赖引入的最小化。

目前的开发体验

1. 冷启动 5秒,热编译 1秒内。以前是 5~10分钟。

2. 改一行代码能解决的问题,真正改一行且发布一次。而不是改 10+ 个项目,按依赖发布 N 次。

3. 新人 10分钟搭建好环境,上手开发。以前每个组件一个 Repo,包赋权都要搞很久。

4. 避免了版本不对齐的问题

对于 2C 产品,不需要多版本多主干分支,但多个 npm 依赖对齐版本也不容易

对于 2B 产品,由于多环境、多版本,会更加复杂,复杂度极高。Monorepo 通过分支来统一内部依赖的版本

5. 工程化升级只需要一次。目前是基于 Lerna 开发的 Pri Monorepo 方案。

当然,这里提及的体验要保持并不容易,开发中还有很多问题要解决。

真正需要解决的问题

并不是把代码放到一起就完了,背后复杂的问题是协同、技术方案、稳定性。比如,如何避免一个人提交代码导致整个产品崩溃?

**包依赖管理**

内部拆分多个子包,每个子包是子文件,可以单独发布 npm,见下图:

阿里数据中台核心产品揭秘 - 天天要闻

内部包管理的核心原则是:

从左向右单向依赖,只能右边引用左边,避免循环依赖

规范还不够,开发插件来自动检测,如果左边依赖右边直接报错

对于开源 npm 的引入,应该更慎重。大部分 npm 的维护时长不超过x年,即使像 Moment.js 这样曾经标配的工具库也会终止维护。可能有 20% 的 npm 是没人维护。但未来如果你的线上用户遇到问题,你就需要靠自己啃源码,陷入被动。所以我们的原则是,引入开源 npm 要三人线下评审通过才行。

**Code Review 文化**

互相 Code Review 能帮助新人快速成长,同时也是打造团队技术文化的方式。

过去几年一直在团队内推行 100% CR,但这还不够。机械的执行很容易把 CR 流于形式,还要分场景来做。

Monorepo 有个风险是一旦有问题就可能是整体的问题。

目前我们的 Code Review 主要分为3个场景:

线上 MR Code Review【1对1】

主题式 Code Review【3-5个人】

大版本发布前集体 Code Review【All】

12,111 次 Code Review 的经验很多,主要是:

及时 Review,鼓励小颗粒度的 MR,不必等整个功能开发完成

代码是写给人看的,鼓励白话文一样的代码,而不是文言文

建立最佳实践(目录树结构、命名规范、数据流规范)。开发一个功能可以有 10 种方法,但团队需要选 1 种并推广

不鼓励炫技,为了未来可维护性。能用简单技术实现,不要用“高深”冷门的技术

强调开发洁癖,追求优雅代码的文化(命名是否易于理解、注释是否完整、是否有性能隐患等)

**工程化建设**

这个过程首先要感谢淘系前端 DEF 工程化团队的支持,在这么多代码的情况下,不断挑战极限升级 DEF 支持我们。

除了制定文档的规范之外,能够自动化工具检查的规范才是好规范。

检查器:ESLint、TS 类型校验、Prettier

语法检查器是推动规范落地的重要方法,ESLint 可以做增量,优化后 git commit 的 pre-hooks 依旧很快。但 TS type check 因为不支持增量就比较慢了,需要搭配 CI/CD 来使用。

Webpack vs Vite

发布使用 Webpack,开发使用 Vite。

开发环境使用 Vite 快速调试,生产环境依旧使用 Webpack 打包。

风险是开发和生产编译产物不一致,这一块需要上线前回归测试避免。

**性能优化**

对于数据产品而言,性能的挑战除了来自于 Monorepo 后资源包的变大,还有大数据量对渲染计算带来的挑战。

性能优化可以分为3个环节:

资源加载:精细化 Tree Shaking,难在精细。Webpack 本身的 Tree-Shaking 做的并不好,不支持 Class method 做 Tree Shaking,所以有时候需要修改代码。Lazy Loading 模块做到按需加载,尤其是图表、SQL 编辑器这类大组件。合理的接口预加载,不要让网络闲下来。

视图渲染:让组件渲染次数降到最低,表格类组件虚拟滚动优化,闲时预加载预渲染。

取数请求:资源本地化缓冲方案,移动端使用 PWA 将 JS 等资源文件和数据缓存到本地。

另外还有性能检测工具,定位性能卡点。计划做代码性能门闩,代码提交前如果发现包体积增大发出提醒。

**数据化驱动架构优化**

身在数据中台,我对数据的业务价值深信不疑。但对于开发本身而言,很少深度使用过数据。

所以 S1 重点探索了开发体验的数字化。通过采集大家的开发环境和启动耗时数据来做分析【不统计其他数据避免内卷】。发现很多有意思的事情,比如有个同学热编译 3~5 分钟,他以为别人也是这样慢,严重影响了开发效率,当从报表发现数据异常后十分钟帮他解决。

另外一个例子,为了保持线上打包产物的一致性,推动团队做 Node.js 版本统一,以前都是靠钉,钉多少次都无法知道效果如何。有了报表以后就一目了然。

阿里数据中台核心产品揭秘 - 天天要闻

目前整个数据化的流程跑通,初步尝到甜头。未来还有很多好玩的分析可以做。

更深层的经验

**效率最高的方式就是一次做好**

每行代码都会留下成本。长远考虑,效率最高的方法就是一次做好。

苏世民说“做大事和做小事的难度是一样的,两者都会消耗你的时间和精力。”既然如此,不妨把代码一次写好。代码中如果遗留 “TODO” 可能就永远 TO DO。客观来讲,一次做好比较难,首先是每个人认为的“好”标准不同,背后是个人的技术能力、体验的追求、业务的理解。

**组织文化技术,相辅相成**

技术架构和组织结构有很大关系,选择适合组织的技术架构更重要。

如果一个组织是分散的,使用 Monorepo 会有很大的协同成本。但组织如果是内聚的,Monorepo 能极大提效。

工程化和架构底座是团队的事情,靠个人很难去推动。

短期可以靠战役靠照搬,长期要形成文化才能持续迭代。

组织沟通成本高应该通过组织来解,通过技术来解的力量是渺小的。技术可以做的是充分发挥工具的优势,让变化快速发生。

**简单不先于复杂,而是在复杂之后**

对于一个简单的架构,总有人会想办法把它做复杂。踩了坑,下决心重构,成功则回归简单,失败就会被新的简单模式颠覆。踩坑本身也是有价值的,不然新人总是按捺不住还会再踩一次。做复杂很容易,但保持简单需要远见和克制。没有经历过过程的磨练,别人的解药对你可能是毒药。

架构不可能一成不变的,我们的图表最开始直接使用 D3、ECharts 很简单,后来定制很多逐渐复杂到难以维护,于是基于 G2 自研 bi-charts 后架构又一次变简单,前后的开发体验可能是差不多的,但背后的技术完全变了。

总结与展望

百万行代码没什么可怕,是一个正常的节点,仍然可以像几万行代码那样敏捷。

现在 Quick BI 已经向千万行迈进,向世界一流 BI 的目标迈进。以上内容更多是工程化相关,把工程化做好目的是想让开发者更专注于业务,没讲的业务挑战其实更多,因为数据分析天生就要与海量数据打交道,性能优化有长期的实践;洞察丰富异样的数据,有很多可视化及复杂表格方面的沉淀,可视化不仅是技术,也是业务本身;手机平板电视等多端展示,跨端适配的挑战。

未来还希望能够把数据分析打造成一个引擎,能够快速集成到办公和商业流程中。

目前的开发模式并不完美,在迭代的过程中,不可避免会产生技术债,架构的优化本质就是在保持可维护性和减少技术债。最近团队在酝酿一次 Redux-Toolkit 的引入,会对取数和数据流有大的升级,有进展再分享。(完)

阿里数据中台核心产品揭秘

科技分类资讯推荐

深圳综改再升级,多个领域有重大利好丨深政一周 - 天天要闻

深圳综改再升级,多个领域有重大利好丨深政一周

本周,由中共中央办公厅、国务院办公厅印发的《关于深入推进深圳综合改革试点 深化改革创新扩大开放的意见》于6月10日正式对外发布,深圳再迎政策利好。《意见》聚焦四大领域,再推出一批改革措施、落地一批创新试验、深化一批开放举措。就在《意见》发布后的第一时间,6月11日,深圳市委常委会召开扩大会议,同时套开市委...
英特尔启动新一轮裁员 - 天天要闻

英特尔启动新一轮裁员

6月15日消息,据外媒Oregon Live报导,英特尔已经于本周向员工发出通知,将从今年7月中旬起,开始裁减位于俄勒冈州Silicon Forest园区的晶圆厂人员,首轮裁员预计将于7月底前完成,而且可能会启动第二波裁员。根据英特尔内部信件,公司正针对Intel Foundry 制造事业部进行重组,并更聚焦于工程和技术职位(例如精简中阶管理...
Synopsys重启部分中国服务,但核心EDA销售仍受阻 - 天天要闻

Synopsys重启部分中国服务,但核心EDA销售仍受阻

据路透社最新报道,美国EDA及半导体IP大厂Synopsys(新思科技)近日已经恢复了在中国的部分服务,但是核心EDA工具仍无法供应。今年5月29日,美国商务部工业和安全局(BIS)向包括Synopsys、Cadence、西门子EDA在内的EDA大厂发出了新的对中国出口管制通知函,该通知函广泛禁止这些厂商在中国销售产品和服务。Synopsys CEO Sas
“不想搞事就选英特尔!”安安稳稳的intel微星全家桶来啦! - 天天要闻

“不想搞事就选英特尔!”安安稳稳的intel微星全家桶来啦!

作为攒机圈的老炮,今天我要给各位推荐一套真正"安安稳稳"的Intel平台配置——以微星全家桶为核心,搭载最新的酷睿Ultra 7 265K处理器、微星MPG Z890 EDGE TI WiFi刀锋钛主板、MPG A1000GS PCIe5电源和MAG CORELIQUID I360 White白色水冷,再配以MAG PANO 100L PZ机箱。这套配置不仅
新能源车保险,为啥又贵又难买?车企“两面派”,保险公司只认钱 - 天天要闻

新能源车保险,为啥又贵又难买?车企“两面派”,保险公司只认钱

最近两年时间,国内新能源汽车的保有量和渗透率都呈现了明显增长的态势,很多新能源汽车,卖得又贵又好,国内市场也成为全球范围内,新能源汽车普及最为快速的市场,甚至没有之一。新能源汽车智能化程度高、费用低、性能表现优秀,成为了很多小伙伴购买新能源汽车的原因,尤其是省
22款车降价,比亚迪真绷不住了?可能在下盘大棋,两个苗头已出现 - 天天要闻

22款车降价,比亚迪真绷不住了?可能在下盘大棋,两个苗头已出现

最近一段时间,关于比亚迪的各种信息,可以说甚嚣尘上,其中首当其冲的,就是比亚迪22款新车大补贴,海鸥等车型的补贴后价格来到了5万级别,最高降价幅度超过了5万元,可以说这是2025年规模最大的一次降价,非常符合比亚迪“火力覆盖”的特性,当然也有一些车企已经跟进,
新增哨兵功能+沙地模式:坦克500 Hi4-Z第二次OTA开启推送 - 天天要闻

新增哨兵功能+沙地模式:坦克500 Hi4-Z第二次OTA开启推送

快科技6月15日消息,坦克500 Hi4-Z的第二次OTA更新已经开启推送,此次更新为车辆带来了两项重要的新功能。第一,哨兵模式和优化后的沙地模式。哨兵模式能够实现全车360度环视监控,全天候为车辆“站岗”。当有人员靠近停留或车辆检测到震动时,该模式将被触发。如果识别到低风险事件,中控屏会弹出告警动画,以此警示可疑人...
曾经爆火的黄焖鸡米饭,他经营了9年,这次的遭遇有点懵 - 天天要闻

曾经爆火的黄焖鸡米饭,他经营了9年,这次的遭遇有点懵

钱老板的黄焖鸡米饭店开在杭州余杭一个写字楼外围说是经营9年了2月中旬有人来做推广结果遇到了问题钱老板:3月1号之前入驻免配送费的,2月28号正式开始开通京东外卖,他们说会自动到账到我银行卡上,不用操作提现。记者:实际上呢?钱老板:实际上自己