写点什么

百度 App 组件化之路

  • 2019-11-28
  • 本文字数:4199 字

    阅读完需:约 14 分钟

百度App组件化之路

组件化是一个老生常谈的涉及面很广的话题,即不是做好一件事而是做好一系列的事情才能达成;其中包含组件化框架在内的各架构层级、构建系统、依赖管理系统、以及配套的防劣化机制与规则规范。

  本文主要基于百度 App 背景、目标和组件化历程来讲述保障并行开发和组件复用的手段,尽量避免过多发散到构建系统、依赖管理系统,以及组件化框架这样的具体子方向。组件化的重要性取决于应用规模、团队规模、产品技术目标;所述内容虽然是从 iOS 平台出发,但方法论与实现路径适用于大部分平台。

背景与目标

百度 App(大型 App)复杂度来源


1.业务规模大:百度 App 技术方向及子方向 70+,单端代码量 180w+;


目标:隔离各组件间影响避免故障蔓延,并控制整体 App 的复杂度;


2.团队规模大:有代码权限的数百人;


目标:保障高效并行开发;


3.公司内部接入业务多:30+,非单纯基础库,与百度 App 关系复杂;


目标:处理接入业务与百度 App 架构及架构中各组件关系,保障快速高效接入与基础能力复用。


4.迭代速度快:3 周一个版本,2 周开发 1 周测试;


目标:避免高速迭代情况下组件化程度劣化。


5.技术形态多:H5、NA、Hybrid、Talos、Flutter 并存;


目标:保障基础能力复用,构建系统支撑。


另外启动速度、体积等准入流程的约束;以及目标的多样性也是大型 App 复杂度来源因素;由背景产生的目标是天生的技术需求,除此之外,百度 App 在不同阶段有不同的产品技术目标。

百度 App 不同阶段的不同目标

1.合作业务三方库复用;单个技术组件输出(最早的需求,2014 年),对单个组件输出来讲,如何避免输出时,拔出萝卜带出"泥";


2.矩阵产品孵化(2017 年~2019);


3.小程序开源复用(2018 年):输出组件兼容不同宿主,保持部分依赖组件可替代性;


目标多样性要求在开发时考虑到各个目标的诉求,在方案设计时尽量避免和这些目标冲突。

重要架构迭代

初始态-2013(钻木取火):


这一时期,百度 App 浏览器角色较重,大家都在一个工程里开发,各业务和基础逻辑交错,没有边界,你中有我、我中有你;UI 架构比较复杂,每个 RD 都要从 App 主入口开始看懂主流程代码,小心翼翼的开发。


这一时期的架构是这样:



这一时期的主要问题有:


  • 一些基础库、甚至开源三方库都会有业务侵入;没有明确分层和防修改机制,入侵成本极低;

  • 首屏各业务间没有容器隔离,牵一发而动全身,极容易互相影响;

  • 对各业务共用的服务(远程配置、端能力)没有服务组件化,if else/switch case 式逻辑无限蔓延

  • 逻辑、资源没有合理归属,数据没有拆分,基础组件对外输出困难;

  • 插件接口层没有体系化建设,稳定性欠佳(fragile);接入业务成为百度 App 里的超级模块,依赖关系难以控制。


2014-2015(蒸汽机时代):


虽然当时团队规模只有几十人,但已经意识到了组件化的重要性;接入业务逐步变多,同时也有部分技术组件对外输出的需求;这一阶段:


  • 首先拆出三方库,粗粒度拆出基础库,归到业务组件下层;百度 App 和接入业务复用这部分基础库;

  • 引入框架容器,对首屏各业务及栈式导航容器中的业务进行隔离;

  • 对新兴的业务组件或需要重构的业务,首先采用组件化模式开发,逻辑、资源、数据各有归属,同时明确外部依赖;

  • 初步制定了依赖规范,禁止层级反向依赖,这一阶段只是规范,没有工具链的强制支撑;

  • 组件除基础库外的依赖通过 Adapter 注入来实现。

  • 这一时期的架构是这样:

  • 这一时期的主要问题有:

  • 组件归属的模糊性,部分组件游离在基础库和业务组件之间,同层组件间的依赖与调用关系不够清晰;

  • 组件间通过 Adapter 进行一对一解耦,虽然有比较明确的外部依赖关系,但解耦效率不高;

  • 主 App 中还遗留端能力接口,与通过插件系统接入的一些 SDK。

  • 2016-2017(电力时代)

  • 这一时期重点建设了组件化框架(Pyramid、SchemeRouter)与分发框架(RemoteConfig、PMS、预取分发),及数据拆分框架(CocoaSetting);进一步保障了各组件能做到逻辑、数据各有归属;

  • 这一时期的架构是这样:

  • 2018-2019(理想态-核能时代)

  • 随着百度 App 组件化程度的提高,主工程逐步壳化,沉淀了大批通用服务;这一时期团队高速发展,加入了多仓库和构建系统的支撑(EasyBox)。

  • 这一时期的架构是这样:

  • 这一时期,组件化框架相对完善,各组件已能做到逻辑、资源、数据各有归属。主工程进一步被弱化;

  • 层级更加明确清晰,游离与基础库层和业务组件层间的通用服务有了归属;组件可以自下而上的对外输出;

  • 整个 App 通过中央仓库的组件列表(Central Repo Specs),经过 EasyBox 组装整个工程;

  • 框架容器加载及系统事件分发统一到轻量级的 AppLauncher;

  • 对接入 SDK,按架构层级属性归属;如仅被某一个业务组件引用,则有这个业务组件负责管理,降低对外的复杂度;

  • 服务层可共享服务相对完善。

  • 组件化的进阶-中台化(星际远航)

  • 中台化的大潮滚滚而来,除云端一体化复用外,对组件化也提出了其他的更高要求。共享组件库+构建系统(EasyBox)合力,已能达到矩阵产品组合输出能力。

组件化的实现路径

自下而上的组件化建设


1、编译隔离、架构分层及层级访问限制机制建立


  • 编译隔离:通过构建系统(EasyBox)提供的编译规则文件明确每个组件的对外接口,明确组件的外部依赖(这方面 IDE 也经常好心办坏事,让组件间可以低成本的随意访问,逐步模糊了组件的逻辑边界,加深了组件间的依赖);

  • 层间限制:通过构建系统(EasyBox)建立反向访问限制,即下层组件不可以访问上层组件;

  • 同层访问:同层组件间也不能无限制调用,通信及访问限制通过组件化 Pyramid 框架来完成;各组件间维持较清晰的接口边界和逻辑边界。

  • 2、三方库规范化与基础库体系化

  • 基础库主要存在以下问题:

  • 没有防修改机制,业务侵入成本低;

  • 交叉依赖问题:同一基础依赖的逻辑归属到同一组件里

  • 基础库要在无业务侵入的情况下经过一定程度的抽象到架构底层,二进制化实行组件负责人制度,并进行体系化建设避免上述问题。

  • 三方库主要存在以下问题:

  • 没有防修改机制,业务侵入成本低;

  • 三方库在一定用户规模或业务规模下,确实存在 bug,而 github 的 push request 不及时或无响应;

  • 所有三方库更新到最新发布版并二进制化避免业务侵入;差异部分明确修改点,通过运行时单独打补丁;对外输出时,明确这一点。

  • 3、建立运行时分发与隔离服务

  • 为避免各组件对共有逻辑、共有数据集中式处理,建立容器及分发机制来分发事件、数据、以及逻辑调用。

  • Pyramid 组件化框架:

  • 这里主要讲 Pyramid 框架的分发作用,Pyramid 将系统事件分发给各子组件;

  • 除此之外,组件化框架还有另外两个作用:

  • 1)Pyramid 框架组件间通信:adapter 一对一方式解耦升级为一对多解耦;

  • 2)它将组件间的强依赖转变为弱依赖,这让技术组件对外输出时,被依赖的组件具有某种程度的可替代性;

  • 端能力:

  • 分离 SchemeRouter 与 SchemeHandler 逻辑,SchemeRouter 归属服务层组件化框架,SchemeHandler 归属各组件;

  • 由于 Scheme 参数不够明确清晰,在百度 App 中 Scheme 主要用于和 H5 的通信,较少用于页面路由;

  • 配置分发服务:将集中解析并调用各业务处理逻辑改造为分发机制,并最终升级为云控服务;

  • 数据拆分服务:配合配置分发服务,将数据拆分到各组件内部管理;

  • 资源/预取分发服务:建立资源/预取分发服务;

  • 框架容器:通过 Tab 导航容器、栈式导航容器将各控制器 UI 数据拆分到各子控制器、事件分发传递给各子控制器;

  • 分发与隔离机制、容器机制是并行开发的重要保障。

4、服务层建立

多业务调用的低依赖组件去业务化抽象成通用服务:账号、分享、云控、统计、性能、AI 等

5、建立组件模型

建立组件模型,各业务模块快速组件化。



  • 通俗的讲,就是指导各业务模块明确功能范围,做到逻辑、资源、数据、私有 SDK 各有归属;

  • 最终每个组件是一个独立的功能单元、逻辑单元、数据及资源管理单元,H5 通信单元,性能量化单元,编译输出单元(1 个或多个)。

  • 为了更灵活的组合输出,组件的接口封装层和服务对接层可以进行不同粒度编译单元拆分;主要目的是分离依赖,满足输出灵活性;

6、业务组件化

按照组件模型,确定业务的功能范围、逻辑与接口边界,快速组件化。

7、劣化控制

组件接口变更、依赖变更、Warning 数变化都会记录通知相关负责人,这些在 Tekes 平台管理;敬请继续关注后续公众号文章;没有防劣化机制,填坑速度永远比不上挖坑速度;一人填坑的速度也永远比不上多人挖坑速度。


收益总结


1.研发效率的提升,主要体现在以下几个方面:


  • 复杂度控制:复杂度控制在组件内部,对外"简单可依赖";

  • 并行开发:组件化框架及各分发服务具备设计时的隔离性,保障大规模并行开发效率;以远程配置为例,原来新增一项开发时间为 4+小时,现在仅需 0.5 小时;效率提升 8 倍+;

  • 复用:为矩阵产品输出轮子;参考百度 App 矩阵产品,复用率都在 50%以上;

  • 编译速度提升:因为组件具有独立编译单元的属性,在编译过程中组件源码和编译产物可以等价替换,所以组件化也为后续组件二进制化打下基础,百度 App 编译速度也平均值从 15 分钟/次优化到 2 分钟/次;

  • **2. 质量上,组件化具备设计时的隔离性,确保单个组件的故障影响范围内敛到自己内部,不会引发整体 crash;


  1. 为启动速度、体积等方向提供量化单位;

  2. 建立健全架构体系,分组件深度优化。**


吴军博士在《文明之光》一书中评价希腊人对世界文明的贡献这样写到:近代自然科学的很多体系都是在古希腊时代奠定的,希腊人在学术研究上有别于东方文明之处不在于一两项科学发明和发现,而在于他们将自然科学各学科分门别类,对每一个学科都建立起一整套系统的体系,在此基础上,演绎或归纳出普遍规律性,即定理或定律,继而成为自然科学各个学科的基石和支柱。

虽然没有这样的高度,但对软件开发来讲,也有异曲同工的作用。

结语

"自己"得来终觉浅,引用一段话作为结语,节选自《每个架构师都应该研究下康威定律》


架构的目标是用于管理复杂性、易变性和不确定性,以确保在长期的系统演化过程中,一部分架构的变化不会对架构的其它部分产生不必要的负面影响。这样做可以确保业务和研发效率的敏捷,让应用的易变部分能够频繁地变化,对应用的其它部分的影响尽可能的小。

参考

每个架构师都应该研究下康威定律:


https://www.infoq.cn/article/every-architect-should-study-conway-law


3 Key Software Principles You Must Understand:


https://code.tutsplus.com/tutorials/3-key-software-principles-you-must-understand--net-25161


本文转载自百度 App 技术。


原文链接:


https://mp.weixin.qq.com/s/P-vREnrw4xGyhiugpzB-1Q


2019-11-28 08:006281

评论

发布
暂无评论
发现更多内容

扩展ADO.net实现对象化CRUD(.net core/framework)

Spook

.net ORM ado

2021Android高级进阶学习资料,已拿意向书!

欢喜学安卓

android 程序员 面试 移动开发

2021Android高级面试题及答案,Android篇

欢喜学安卓

android 程序员 面试 移动开发

UC生态系统APP开发详情

你不知道的 Linux 使用技巧

学神来啦

Test

bobcatzoo

蜜蜂圈软件开发|蜜蜂圈APP系统开发

AI框架中图层IR的分析

华为云开发者联盟

mindspore IR

百度工程师手把手教你实现代码规范检测工具

百度开发者中心

百度 代码规范

【全球软件大会】华为前端工程师分享:华为云官网的智能化实践

华为云开发者联盟

算法 智能化 华为云官网 全球软件大会 内容分发

圆梦腾讯之路!6面阿里、5面字节、4面腾讯,终斩腾讯Offer

Java 编程 架构 面试

区块链技术如何赋能医学成像?

CECBC

7月日更,红心向党,党员入驻,即送马克杯~

InfoQ写作社区官方

7月日更 热门活动

话题王者第三季,舍我其谁~

InfoQ写作社区官方

话题讨论 热门活动

anyRTC视频连麦demo上线啦!

anyRTC开发者

音视频 WebRTC 直播 视频直播 直播连麦

秀出天际的SpringBoot笔记,让开发像搭积木一样简单

泪目!跳槽太不容易,蚂蚁金服三轮面试,四个小时灵魂拷问

Java 面试

我看 JAVA 之 线程同步(下)

awen

Java synchronized JOL 锁升级

千亿级数据迁移 mongodb 成本节省及性能优化实践(附性能对比质疑解答)

杨亚洲(专注MongoDB及高性能中间件)

MySQL 数据库 mongodb 架构 分布式数据库mongodb

在C++中,你真的会用new吗?

华为云开发者联盟

c++ 内存 new new operator operator new

区块链的正确应用方式与前景

CECBC

Tubacle挖矿系统APP开发搭建

Bi Token质押挖矿软件系统开发方案

测量电压调节器输出纹波和开关瞬变的方法

不脱发的程序猿

硬件研发 输出纹波测量 开关瞬变测量 电源测试 测量电压调节器

35K成功入职:蚂蚁金服面试Java后端经历!「含面试题+答案」

Java 编程 程序员 架构 面试

GaussDB(for Redis)揭秘:Redis存算分离架构最全解析

华为云开发者联盟

redis 华为云 GaussDB(for Redis) 存算分离架构 中国系统架构师大会SACC

企业资产数据大屏,打破固有管理思维僵局,杜绝资产无效流失

一只数据鲸鱼

数据可视化 资产管理 金融资产 金融大屏

看CarbonData如何用四招助力Apache Spark

华为云开发者联盟

spark Apache Spark ACID CarbonData 分布式集群计算框架

基于 BDD 理论的 Nebula 集成测试框架重构(下篇)

NebulaGraph

分布式数据库 测试 图数据库 BDD

内蒙古公安重点人员管控研判平台建设方案

智安小区建设,智慧安防小区改造建设方案

百度App组件化之路_语言 & 开发_百度App技术_InfoQ精选文章