B 站 Android 代码库的演进历程
早在2012年,B 站 Android APP 便已上线。当时开发者不过一人,而如今,业务线众多、隶属不同团队的Android 端开发人员数以百计。从单兵作战到百花争鸣,代码库的组织管理也随之经过数次的改革、演进。
单仓库
2014年底,Android 端的常驻开发人员一只手也数的过来。业务发展迅速,为追求效率,方便管理,所有代码都在一个仓库中,甚至包括第三方的、开源的代码(个别用 git submodule 管理)。Clone下来导入 Eclipse 就可以开干。
到大约15年中旬,开始使用 Android Studio,得益于 Gradle 的项目管理理念,分出了多个 library module。外部依赖使用 maven。也是这一期间开始搭建了内网 maven 服务。
这期间代码库组织结构是:单仓库 + 个别 git submodule。
这种组织方式好处显而易见:
- 项目结构简单:随时随地 clone 下来导入 IDE 即可以开始开发,代码所有人可见,没有额外的限制。
- 方便快速迭代:改动可以快速入库,适合小团队,review方便,改动透明
但是,约莫到16年中,业务发展,新团队纷纷成立,招聘要求降低人员迅速膨胀。这种小而美的代码库已经不适用了,主要有以下缺点:
- 代码结构混乱,模块之间依赖关系混沌:倒不如说是技术债,前期的疲于业务迭代,以及没有及时的规划出好的代码层级架构,如今人员纷杂水平不一,之前追求的“没有限制”反而诱发了恶果
- 编译时间变长:业务增速发展,代码量爆炸式增长,单机编译越来越慢,开发幸福感跌到谷地
多仓库
一个字:“拆”。
单仓库里,所有能切出去的、划走的,甭管横的来按业务分、竖的走按层级分,都拆出去,最好仓库中只留一个光杆app模块,最终只剩下拆无可拆的模块,所有依赖的模块都预先编译好上传到maven。期间冠名曰:组件化。
约到17年中,这些拆出来的模块,一些独立为仓库,一些按所属层级聚拢到一起合为一个仓库。业务模块为了快速调试,一般会写一个demo app 模块用作测试。
此时代码库组织结构是:多仓库。
好处是:
- 隔离性十足:按业务划分的模块所属仓库,倚托于gitlab权限管理,“外人”决计无法修改自己的代码。
- 依赖关系清晰:自上而下,由外而内。更容易做到高内聚低藕合。
- 编译速度快:大部分依赖都预编译后上传到maven,比起之前从源码编译的速度不知道快到哪里去了
但是…
随着业务线发展、人员的进一步扩张,不到一年时间,多仓库的缺点也快速暴露了出来:
- 维护复杂:尤其是模块之间有依赖关系时,修改依赖版本号是个重要、重复且容易出错的操作。且不便于review不便于快速暴露修改导致的兼容性问题,更进而影响其它模块进度。历史包袱重,比如由于离职等原因导致责任人丢失,非常难梳理出所有模块的修改历史,多个团队协同开发的时候难上加难
- 代码重复:由于代码分散仓库存放,天然的隔离更容易产生互相拷贝的问题。另外也容易产生相同功能的重复开发,如由不同的人作一个类似的模块,但彼此不知。
- 过分隔离:由于团队的不同,协作工作流程长且所涉人员多,沟通成本非常之高,开会从早到晚,一线开发人员一般只转注于自己的模块,当一个螺丝钉,无法从项目整体视角看问题,进而又导致项目结构趋于混乱、依赖关系复杂化。
大仓
“大仓”—— 一个来自B站 Go 团队率先实践过切实可行的方案映入眼帘。
大仓,来自英文 Monotonic Repository (简称monorepo)。望文生义,即一个仓库,包罗万象海纳百川。源自西方大厂们(Google/Facebook/…)一致叫好的实践良久的代码管理方案。
说到底其实就是一开始的“单仓库”的进化版。与纯粹的“单仓库”不同的地方是,有众多的工具链来维持大仓的日常开发,而非人力,是工程师文化中“不重复劳动”的极致化体现。
关于大仓,这里不费篇幅去过多解释了,请自行 Google
目前(2019/1/3),因为站在巨人的肩膀上,先行者已经准备好了一部分的工具链(gitlab-ci, saga等),B站 Android 团队已基本完成大仓的组建。
依然遵循之前规划的架构以及组件化的思想,将目录结构约定为形如:
<root dir> ├── app │ ├── main │ ├── live │ ├── bangumi │ ├── ... │ ├── common │ ├── ... │ ├── framework │ ├── ... │ └── entrance
依赖关系约定,app -> common -> framework
好处自不必多说,先说其局限性:
- 庞大而臃肿: clone项目和导入所有代码对开发机器是个挑战(为此我已经更换新款MBP~~)。但可以通过编译配置以及git sparse-checkout解决
- git-flow混乱: 因为之前不同团队不一样的git-flow,先天的认知不一,另外个别人对git操作理解不深,会导致出现一些人为因素上的合丢代码
对于Android 大仓如何组成,如何解决上述问题,下一篇会给大家分享 欢迎关注
总结
所谓“分久必合,合久必分”,单仓库也好,多仓库也罢,都是为了解决业务上的问题而生。
从来没有放之四海皆准的方案,千万不要脱离业务谈架构,选择适合自己团队的方案才是好方案。
早在2012年,B 站 Android APP 便已上线。当时开发者不过一人,而如今,业务线众多、隶属不同团队的Android 端开发人员数以百计。从单兵作战到百花争鸣,代码库的组织管理也随之经过数次的改革、演进。
单仓库
2014年底,Android 端的常驻开发人员一只手也数的过来。业务发展迅速,为追求效率,方便管理,所有代码都在一个仓库中,甚至包括第三方的、开源的代码(个别用 git submodule 管理)。Clone下来导入 Eclipse 就可以开干。
到大约15年中旬,开始使用 Android Studio,得益于 Gradle 的项目管理理念,分出了多个 library module。外部依赖使用 maven。也是这一期间开始搭建了内网 maven 服务。
这期间代码库组织结构是:单仓库 + 个别 git submodule。
这种组织方式好处显而易见:
- 项目结构简单:随时随地 clone 下来导入 IDE 即可以开始开发,代码所有人可见,没有额外的限制。
- 方便快速迭代:改动可以快速入库,适合小团队,review方便,改动透明
但是,约莫到16年中,业务发展,新团队纷纷成立,招聘要求降低人员迅速膨胀。这种小而美的代码库已经不适用了,主要有以下缺点:
- 代码结构混乱,模块之间依赖关系混沌:倒不如说是技术债,前期的疲于业务迭代,以及没有及时的规划出好的代码层级架构,如今人员纷杂水平不一,之前追求的“没有限制”反而诱发了恶果
- 编译时间变长:业务增速发展,代码量爆炸式增长,单机编译越来越慢,开发幸福感跌到谷地
多仓库
一个字:“拆”。
单仓库里,所有能切出去的、划走的,甭管横的来按业务分、竖的走按层级分,都拆出去,最好仓库中只留一个光杆app模块,最终只剩下拆无可拆的模块,所有依赖的模块都预先编译好上传到maven。期间冠名曰:组件化。
约到17年中,这些拆出来的模块,一些独立为仓库,一些按所属层级聚拢到一起合为一个仓库。业务模块为了快速调试,一般会写一个demo app 模块用作测试。
此时代码库组织结构是:多仓库。
好处是:
- 隔离性十足:按业务划分的模块所属仓库,倚托于gitlab权限管理,“外人”决计无法修改自己的代码。
- 依赖关系清晰:自上而下,由外而内。更容易做到高内聚低藕合。
- 编译速度快:大部分依赖都预编译后上传到maven,比起之前从源码编译的速度不知道快到哪里去了
但是…
随着业务线发展、人员的进一步扩张,不到一年时间,多仓库的缺点也快速暴露了出来:
- 维护复杂:尤其是模块之间有依赖关系时,修改依赖版本号是个重要、重复且容易出错的操作。且不便于review不便于快速暴露修改导致的兼容性问题,更进而影响其它模块进度。历史包袱重,比如由于离职等原因导致责任人丢失,非常难梳理出所有模块的修改历史,多个团队协同开发的时候难上加难
- 代码重复:由于代码分散仓库存放,天然的隔离更容易产生互相拷贝的问题。另外也容易产生相同功能的重复开发,如由不同的人作一个类似的模块,但彼此不知。
- 过分隔离:由于团队的不同,协作工作流程长且所涉人员多,沟通成本非常之高,开会从早到晚,一线开发人员一般只转注于自己的模块,当一个螺丝钉,无法从项目整体视角看问题,进而又导致项目结构趋于混乱、依赖关系复杂化。
大仓
“大仓”—— 一个来自B站 Go 团队率先实践过切实可行的方案映入眼帘。
大仓,来自英文 Monotonic Repository (简称monorepo)。望文生义,即一个仓库,包罗万象海纳百川。源自西方大厂们(Google/Facebook/…)一致叫好的实践良久的代码管理方案。
说到底其实就是一开始的“单仓库”的进化版。与纯粹的“单仓库”不同的地方是,有众多的工具链来维持大仓的日常开发,而非人力,是工程师文化中“不重复劳动”的极致化体现。
关于大仓,这里不费篇幅去过多解释了,请自行 Google
目前(2019/1/3),因为站在巨人的肩膀上,先行者已经准备好了一部分的工具链(gitlab-ci, saga等),B站 Android 团队已基本完成大仓的组建。
依然遵循之前规划的架构以及组件化的思想,将目录结构约定为形如:
<root dir> ├── app │ ├── main │ ├── live │ ├── bangumi │ ├── ... │ ├── common │ ├── ... │ ├── framework │ ├── ... │ └── entrance
依赖关系约定,app -> common -> framework
好处自不必多说,先说其局限性:
- 庞大而臃肿: clone项目和导入所有代码对开发机器是个挑战(为此我已经更换新款MBP~~)。但可以通过编译配置以及git sparse-checkout解决
- git-flow混乱: 因为之前不同团队不一样的git-flow,先天的认知不一,另外个别人对git操作理解不深,会导致出现一些人为因素上的合丢代码
对于Android 大仓如何组成,如何解决上述问题,下一篇会给大家分享 欢迎关注
总结
所谓“分久必合,合久必分”,单仓库也好,多仓库也罢,都是为了解决业务上的问题而生。
从来没有放之四海皆准的方案,千万不要脱离业务谈架构,选择适合自己团队的方案才是好方案。
欢迎加入Android进阶交流群;701740775。进群可免费领取一份最新技术大纲和Android进阶资料。请备注csdn
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
netty原理分析
Netty简介 Netty是一个高性能、异步事件驱动的NIO框架,基于JAVA NIO提供的API实现。它提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。 作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,一些业界著名的开源组件也基于Netty的NIO框架构建。 Netty线程模型 在JAVA NIO方面Selector给Reactor模式提供了基础,Netty结合Selector和Reactor模式设计了高效的线程模型。先来看下Reactor模式: 2.1 Reactor模式 Wikipedia这么解释Reactor模型:“The reactor design pattern is an event handling pattern for handling service requests delivered concurrently by one or more in...
- 下一篇
RocketMQ源码分析之RocketMQ事务消息实现原理中篇----事务消息状态回查
在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 上节已经梳理了RocketMQ发送事务消息的流程(基于二阶段提交),本节将继续深入学习事务状态消息回查,我们知道,第一次提交到消息服务器时消息的主题被替换为RMQ_SYS_TRANS_HALF_TOPIC,本地事务执行完后如果返回本地事务状态为UN_KNOW时,第二次提交到服务器时将不会做任何操作,也就是说此时消息还存在与RMQ_SYS_TRANS_HALF_TOPIC主题中,并不能被消息消费者消费,那这些消息最终如何被提交或回滚呢? 原来RocketMQ使用TransactionalMessageCheckService线程定时去检测RMQ_SYS_TRANS_HALF_TOPIC主题中的消息,回查消息的事务状态。TransactionalMessageCheckService的检测频率默认1分钟,可通过在broker.conf文件中设置transactionCheckInterval的值来改变默认值,单位为毫秒。 接下来将深入分析该线程的实现原理,从而解开事务消息回查机制。 Transactiona...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7安装Docker,走上虚拟化容器引擎之路