7 月 25 日,又拍云 Open Talk 第十一期《永不止步:移动应用架构的重塑与优化》再次抵达上海,邀请到“in”iOS 负责人白菜、“扇贝”产品与技术负责人吴焱红、“饿了么”移动端负责人胡彪,分享移动应用背后的架构演进之路。“饿了么”移动端负责人胡彪在活动上作了题为《饿了么?——解决千万人吃饭问题的移动架构》的分享,以下是分享实录:
感谢又拍云提供这样一次机会。我今天和大家分享一下饿了么移动端的一些架构设计。
下面我们会谈及整个移动端架构,同时会说一些饿了么未来的规划。饿了么从 09 年发展到现在,从 CEO 自己的小店送外卖,到现在日配送超过 300 万、我们的蜂鸟和第三方物流人员超过 3 万。这种情况下,用原始的技术是满足不了需求的,因此我们一直在对架构进行调整。
寻找适合的库
请看上边这张图。图中最下面 System&Vendor 指的是不管安卓还是 IOS,我们引用第三方 SDK。当时为了解决问题,涉及到 IOS 和安卓我们通常会一个做网络,一个做打印机的设备连接,那时候由于需要开发多套,导致我们有两批不同的测试人员,还要有人不停地去维护一些关键的服务点。所以我们做跨平台的开发,为了日后操作简便。大家都清楚,安卓 JNI 代码苦于到不同的平台上,我们通过 C 语言和 C++,IOS 通过相应的技术可以实现。
另外我们有 N 多的 Libranes,都是独立的库,做安卓把包放在 Libranes,把一些代码工程托到项目里面导致工程特别大,Xcode6 之前编译一个 framework 其实是比较麻烦的,安卓也是一样,需要下很多的脚本。08 年我们开了这样一个库,一开始不是很稳定, Libranes 放上去都要下一些 libra,每一个版本升级的时候,这样的垃圾有上百 M,安卓要几十个这样的 Libranes,编译的世界安卓要写一个脚本,大家挑起来很痛苦,依赖这样的运行环境很麻烦。
独立私有库建立
后来我们遇到 Pods,可以用 Maven 管理 Libranes,我们发现这虽然很好,但是并不能满足我们所有的需求,我们要建立自己私有的仓库,所以大家可以看到,我们每一个都是一个独立的私有的仓库,现在我们所有的都是独立的 Gitlab 仓库, NET,TCP 一系列的网络请求,以后我们还有一系列网络通信,做移动和网络,前端和后端要有缓存。
我们用 React-native,客户在这边可以学,但是往往网上的框架要么只能缓存图片或者文件,很难达到所有需求吻合,比如我想要自己的缓存,网上的框架只能缓存图片,我要缓存文件怎么办,我要缓存我们的接口数据怎么办, APP 不好的情况下,客户要浏览使用,不能保证每一个请求都可以成功,那么我们是不是要多套不同的策略大家应该非常清楚了。我最近把不适用的按照双向列表清除,现在可以缓存所有东西,包括从每一个接口的数据,我们的图片文件等,只要去请求都可以通过我们的缓存读取,每一款 APP 都是一个 Presenter。
LBS,现在的 LOG,不管是用户的操作还是访问的路径地包括用户的网络请求都可以记录下来,发现错误的时候分析。 TR 这个东西本身是非常古老的,在国内近几年,我们要看用户关键的新闻页面访问,日活月活等。这么多用户点击,系统是不是非常大,是不是饿了么 APP 占用很多的缓存、内存?不是的,作为研发的团队大家应该都很清楚,我们遇到一个问题可以解决,但如果 A 界面发生了一个问题根本不是在 A 而是在 D 页面,虽然是偶然性的 BUG,写了 N 多的步骤,不知道这个 BUG 是什么, BUG 就开始扯皮。这样的事情每一个开发都会遇到,不仅你郁闷,测试的人也郁闷,所以只要说你发生了问题就可以了。
域名升级方案
去年的时候国内运营商集体光纤出问题,主要是 DNS 出了问题,国内的大量的 APP 没有办法访问了,携程出名了,因为腾讯百度都不能访问的,携程安然无恙,完全可以访问。简单解释一下,我和服务器是通过 IP 访问,携程的每一个 APP 不仅有域名还有 IP,其他的主站不能用了,携程依然有用。做移动端都清楚,我无法解析这个域名,有可能会使得一些本地的服务商,或者有一些设备的 DNS 域名无法解析,大量的错误只能有一些补选的方案,假设有一个 APP 云,如果我们有备用或者域名升级的策略我们可以紧急 APP 云。
要在安卓做 Security 业内也有很多方案,也有一些专业脱壳团队,我们还没有推上应用,在做内部的测试,我们升级可以包含 Napos 的升级,APP 的升级,所有的升级都是通过这个;Router 就是相当于写了我们自己虚拟的 DNS 服务,所以可以看到我们所有的 APP 业务线,页面的跳转全部通过 Router,甚至我们现在计划所有的东西通过 Router 进行通讯,定义好我们的 System&Vendor,通过动态变换,比如拉开我们的蜂鸟,因为正常点击会有陪送员的消息,说哪个人在送。
我们之前考虑过,我们在 DB 这块,不管是安卓还是 iOS 都是基于 Sqlite,只有 100 多 KB 的两个文件,可以完全依赖自己的东西,这样可以做到两边的存储做到一致,包括所有异常的处理等等。
上面是一些我们的 APPF,这个里面并不是所有的内容都实现了,我画了一些虚线,我们的规划底层架构搭完之后上面都是我们的,可以给我们的底层打一个基础包,只要在我们的基础打一个新的完整的包就可以看到我们的饿了么,不同的 APP。我们的主要的 APP 目前是外卖,我们的 Napos,蜂鸟,是我们现在的对外公开的配送平台,现在日均 100 万。另外每天有 20 到 30 万百度外卖和美团外卖,具体统计数据不是很详细,估计有 130 万左右。
MVC 和 MVVM 的历史
简单说一下 history,我们最早的时候也在用 MVC ,这个东西是已经在软件行业已经证明是非常成功的架构模式,但是随着业务的增长,会发现 MVC 不能满足我们现在的需求,做 iOS 或者安卓都清楚,所有的 VIEW 会比较弱,特别是有 IPOD 之后,我们都会在代码写自动声称自己的 VIEW,不管是安卓还是 IOS 都会随着业务的增长无限增大。这个时候怎么办,它本身就是 MVC 的经典结构,怎么把他拆开。我相信有一句话很对,每一个成员都存有改变世界的,别人写 1000 行能不能重构,每个办法不能超过 80 行,最宽不能超过 200 个字符,因为后台比较大,要求送一下,每一个方法不能超过一屏。如果我一屏看不完就要回去重写。
既然是安卓系统,MVC 的承担就是 view 这个时间怎么进行 MVP,我真正的业务逻辑层全部放在 P 里面,大家都很清楚,用户只是想看到界面,如果一款 APP 是给老年人读书的,字搞大一点是关键,你做得很酷,view 只是展示而已。这样写是不是 P 就很大了,因为全部到了 P,把 V 隔离了,我们能不能用 MVVM,它和 MVP有什么区别吗?我个人认为区别不大,但还是有优点的。我现在做的是 IN 2.0,如果专门针对女性,或者针对小孩,可以迁移吗?不需要做改动,只要把 UI 重新画一遍就是一个新 APP。
单一职责原则在架构中的重要性
设计有五大基本原则,第一个就是单一职责原则,我们面向接口编程,确定如何修改封闭,拓展、开放。如果你用一个方法做了两件事情,哪怕写了只三个代码,我要如何知道你要做什么呢?比如说先上传数据再去拉取数据,为什么这么做?上传数据和拉取数据是两个不同的事务。一个 Conclusion 就做一个明确的事情,我只要看这个就知道你是干这个活的。
所以单一职责原则要明确区分每一个概念,概念绝对不混淆。同样一个 APP,每一个 APP 都去写一个,我的代码不是可以重用吗,有这么多的方法冗余。不该暴露的不暴露叫 O 编程,通过分层之后把隐藏操作全部隐藏起来,整个逻辑给反编译很难。
我们团队接到第三方专门做框架的团队时,我一般都不直接用。举一个例子假如我用友盟或者腾讯,GA 的报表漂亮,可不可以全部转过去是先要测接口的,如果 MVP 和 MVN 都是独立的,职责单一,测试目标也单一。
路漫漫其修远兮,吾将上下而求索。