斗米客户端的架构思想

背景

随着移动互联网产业的兴起,各式App层出不穷,技术方案多种多样。同样,我们也面临了各式各样的问题,比如产品如何开发能够更快速迭代上线,如何使运营推广更灵活,如何降低研发成本,提高研发效率和质量。随意产品开发的深入,我们越来越迫切寻求探索这些问题的解决方案。

经过这些年在APP端、浏览器内核、H5、server端研发经验的积累,2015年我在斗米的客户端产品上首次提出了以URD驱动webox的客户端平台化架构思想,并经过两年时间多个产品的探索实践,我认为该APP端的架构思想可正式对外分享。由于篇幅原因,今天我只从架构思想和原理上进行分享,而不对实现分享,希望能够给正在探索同样问题的朋友们带来一些思考和灵感。

目前行业内APP可分为三大类:Web App、Native App、Hybrid App。以下我将围绕Hybrid APP的架构思想展开分享,至于为什么使用Hybrid App来分享,后文会阐述这三种App的优缺点。当然,该架构思想也同样适合纯 Native App。

为了更好理解本文,欢迎下载斗米兼职客户端体验,斗米客户端的H5化已达到90%以上。

架构思想总述

先简单说明下URD和Webox概念

  • URD是统一资源调度器,英文名Uniform Resource Dispatcher。它是一个用于跨平台、跨端进行资源调度的字符串,允许用户对任何(包括本地和互联网)的资源通过特定的协议进行交互操作。
  • webox用于承载内容页面统称为框(webox),全称Web Box。webox包含Blockbox和Browser

解决的问题

1、跨平台、架构一致性

现阶段主流的移动OS当属Android和iOS,大多数产品都会覆盖这两个平台。因Android和iOS的平台机制原因,造成在这两个平台的技术架构上产生了分歧。逐渐地,这两个平台的APP就相对独立,实现方案的也有了较大的差异化,系统性技术方案不好落地,沟通、维护成本逐渐变大,这也是架构师们经常头疼的一件事。

有没有遇到这样的场景:

  • 人力资源不足,我们希望在一个平台上实现,然后可以在其他平台上运行
  • 新出了一个平台,比如微信小程序,业务需要重新开发,如果业务能够复用多好
  • 如果公司有多个产品,我们需要基础服务复用,架构一致,降低沟通成本
  • iOS和Android在产品的技术实现上不一致,这时候,服务端需要做这两个平台的兼容。
  • 有时候,对外运营推广的接口不统一,市场就需要对这两个平台单独做推广

因此,遇到这些问题的抓狂,我们认识到跨平台、架构一致性的解决方案,对我们来说是何等的重要。

2、产品迭代快,快速发版

我们都知道APP发版不是一件容易的事,需要把包传给各个渠道。若是紧急出现重大bug,这时候我们就需要重新打好所有的渠道包,重新走发版流程,这对各个团队来说是件痛苦的事。

产品能够快速发版,甚至只需要热更新即可。这是产品快速试错,打击竞品的一把利剑。
在斗米的各客户端中,在APP不需要发版的前提下,可以使用DEK发版。DEK是一种用于热更新的包,可快速上线,并且跨平台(iOS、Andoid共用),就像web上线那么简单灵活。
APP的发版节奏一般是三四周左右,而DEK发版,如果只是fix bug的话,一两天即可完成发版,若是需求发版一周以内即可完成,

3、运营推广更灵活

URD是这套架构方案的核心驱动力,更是运营推广的重要工具。

  • 场景1:端内运营
    1、一般运营活动的落地页是web页面实现,想从活动落地面点击跳转到APP的某产品详情页(或者其他任何APP的页面),在有了URD机制后,运营推广部门不需要给客户端团队提需求实现,只需要使用URD即可跳转到APP的任何页面。
    2、当web嵌入到客户端内,当有发现有涉及到我们客户端已实现的页面(如详情页),会自动302跳转到APP页面,提高了用户体验。

    当然我们使用cookie的机制,端内与web的登录态也会互相同步

  • 场景2:端外运营
    当我们和第三方合作业务时,在第三方app或者第三方web站,我们可以提供URD给对方,URD具备调起app并且跳转到APP某页面的能力

4、解耦、扩展能力

URD与webox的相结合,使得app的解耦和扩展能力极强。URD是驱动力,能够做到跨平台页面调度,比如H5调度webox,Native调度webox,服务端调度webox,端外调度webox,webox内的内容也可跳到端外的能力;而webox是承载页面,承载内容的容器,内容使用DEK部署,DEK可热更新。整套机制跨平台,灵活度高,解耦和扩展能力强。

5、降低成本

研发成本的降低,在这套架构上体现得较为明显。业务开发,以JS言语为主,Native主要负责框架、性能相关的支持。而JS是一门跨平台,而且扩展性良好的语言,在Hybrid App的开发人力相对于纯Native App的开发人力上可缩减一半

APP分类

目前APP大致分成三类

Web App

定义: 将所有功能都放在Web上展现,运行基于本地浏览器。在此将给Web简单的套一层App外壳的应用也归入Web App。完全采用HTML/CSS/JS编写,专为触摸操作进行了优化。目前iOS已禁止简单的套壳App上架。

优点: 开发速度快,跨平台,成本低,实时迭代用户无需更新

缺点: 网络速度要求高、服务器压力大,系统级别API调用难度大,用户体验差、用户留存度低

Native App

定义: Native App是基于手机本地操作系统并使用原生语言编写的 。因为位于平台层上方,向下访问和兼容的能力会比较好一些,可以支持在线或离线访问,消息推送或本地资源访问,摄像拨号功能的调 取。但是由于设备碎片化,App的开发成本要高很多,维持多个版本的更新升级比较麻烦,用户的安装门槛也比较高。

优点: 用户体验佳、交互风格与系统吻合,节省流量,可访问本地资源,速度快,用户留存度高

缺点:成本高,版本迭代慢,需要过审

Hybrid App

定义: 介于Web App与Native App的一种折中方案,底层(框架)部分由iOS/Android开发人员处理,上层(内容展现)部分由Web前端人员处理,用户界面操作逻辑及部分静态资源驻留本地,使得Web App可以对操作迅速反应并在很大程度上实现离线访问。
Hybrid App分为四种:单View混合型、多View混合型、web主体型、多主体共存型(灵活型),点这里看百科

多主体共存型Hybrid App能够实现趋近于原生App的体验。
以下对多主体共存型Hybrid APP说明优缺点。

优点:具有跨平台、用户体验好、扩展性好、灵活性强、易维护、规范化、具有Debug环境、彻底解决跨域问题。

缺点:多一端的团队参与就多一些沟通成本,如接口的沟通,有js与Native的接口,有js与server的接口

架构解析

说到斗米客户端的架构,不得不简单介绍一下kerkee。
斗米客户端基础框架使用的是kerkee框架(http://www.kerkee.com),kerkee是一个多主体共存型Hybrid框架,具有跨平台、用户体验好、性能高、扩展性好、灵活性强、易维护、规范化、集成云服务、具有Debug环境、彻底解决跨域问题。

整体结构图

整体结构分成以下几部分,斗米客户端会把这些基础能力封装到DoumiFramework中,便于其他项目的使用。

  • Application层:主要有URD架构和Webox容器架构,以及一些业务模块,Webox容器在架构思想形成的前两个版本叫作多框一Browser,是不是很通俗易懂,当时还未对容器构建模型。
    “多框一Browser”是用来加载本地页面和web页面的容器。后来发现“多框一browser”过于随意性,对框的定义是根据功能来区别,比如加载首页就叫首页框,加载详情页就叫详情页框,加载列表页就叫列表框等等,当页面的种类越来越多的情况下,框也就越来越多,就造成了泛滥,没有统一的类型,沟通上的成本也越来越大。后来也对框进行建模,建立规范,才有了今天的Webox,后面会介绍。
    URD也是这套架构的核心驱动力,URD是基于RFC 3986规范而制定的一套跨平台规范。URI相信大家都能理解和使用,但URD不是URI,只是在调用和使用层面和URI的用法一样。

  • API层:这一层很重要,它是JS与Native交互的API接口。这层的API可以重写kerkee中功能。比如你看kerkee中实现的XMLHttpRequest(简称XHR)实现的不好,那你就可以重写XHR接口,完全取代kerkee中的XHR模块。这层的接口可以使用静态类,也可以使用对象,具体用法可以查看kerkee的使用。

  • kerkee Framework:是我早年实现的一套Hybrid App框架,目前相对稳定。这个lib就不多说了,基本这个lib之上,还有个kerkee plus,它的作用封装了热部署,以及一些功能接口。具体细节可以去网上看 http://www.kerkee.com
  • Intelligent Data Engine:这是数据层,把所有的数据逻辑圈在这层内,它定义了数据请求来源,数据运行逻辑,形成数据流。
    举个例子,它能够提供给Application层,不管数据是从缓存读取还是从离线数据库读取或者是从server请求。而Application层不需要关心数据来源。
    一般来说,对于APP开发,业务稳定下来后,数据层一般变化得较少,变化较多的是用户体验层(Application层),有了数据层后,app的结构就清晰起来。

安全策略

我们在数据的安全方面下了很大功夫。
AccessToken机制
先介绍一下名词:
DEK加密算法:自研发的加密算法,稳定性高,安全性较好,现在整个斗米的api接口基本都基于此进行加密
DeviceToken:设备唯一标识,由我们自研发的一套设备唯一标识
AccessToken:访问接口所需要和token,它由client发起,动态改变,每次发起请求都会生成不一样的AccessToken,就和银行的eToken类似的原理,就是以下图片这东西

AccessToken包含了DeviceToken等信息,经过DEK加密算法处理后,再进行上报。如果请求的数据中没有AccessToken或AccessToken校验不通过,则不会下发数据。

附加:
我们对所有的请求都使用了https,为了安全,我们损失了一些请求接口的性能作为代价。

Webox

用于承载内容页面统称为框(webox),全称Web Box。Webox包含Blockbox和Browser。
根据承载的区块上来区分:“Blockbox”所承载的内容可以是Native组件,也可以是H5组件(还可以是H5 Page),而Browser所承载的内容只能是H5 Page
根据区块的路径来区分:“Blockbox”不仅支持相对路径Path,也支持绝对路径Full Path和标准URL;Browser只支持URL。

举例:

Blockbox:通用框(common blockbox)、首页框、详情框、列表框、登录框等
Browser:用来承载Web页面的框

URD

什么是URD

字面上的URD
URD是统一资源调度器,英文名Uniform Resource Dispatcher。它是一个用于跨平台、跨端进行资源调度的字符串,允许用户对任何(包括本地和互联网)的资源通过特定的协议进行交互操作。
URD协议沿用URI规范(RFC 3986规范),文档地址:http://www.ietf.org/rfc/rfc3986.txt

URD格式
scheme://action/path-encoding?query#fragment

URD和URI的区别
URI是以一种抽象的,高层次概念定义统一资源标识;
URD所定义的协议是在URI之上,URD是具体资源调度的方式,可用资源的分发调度。URD不仅是一种URI的表现形式,它还可以嵌套URL和其他URI。

架构上的URD
URD是一套客户端技术架构,也是一门技术哲学,是客户端的驱动力,是webbox内容的身份标识。
具备了跨平台调度页面、多层嵌套、数据回传、单向页面依赖调度、URD302等特性

重要特性

跨平台调度页面:H5可以调度webox,Native调度webox,服务端调度webox,端外调度webox,webox内的内容也可跳到端外的能力

多层嵌套:URD页面可以调度另一个URD页面

数据回传:通过URD可以传递数据(包括透传数据)和回传数据

单页面依赖调度:
使用例子来说明,比如从H5某个页面发起URD去调度用户钱包页面,而钱包页面又依赖于登录,对于调起方来说不需要知道进入钱包是否需要登录或是否已登录,URD会自动调起登录进行登录,然后再自动去钱包页面。

支持302跳转
URD302跳转与http的302类似,发起一个URD302以后,会销毁发起方的页面。
场景:有时候我们客户端嵌入第三方web页,但第三方web页又没有使用URD调用客户端中的页面。这时在web页中使用URD302,browser会自动识别client中已有的webox页面并自动跳转到native页面,当用户点击后退时,也会退回上一层web面,这样能够提高用户体验。
举个例子:AWeb页面要去BWeb页面,BWeb页面在client中有对应的BClient页面。AWeb页面跳转打开BWeb页面,在BWeb页面中会发起URD302,这时URD就会跳转到BClient页面,同时销毁BWeb页面,当用户需要后退时,只会回到AWeb页面

URD导图

为什么要用URD

URD是一个跨平台的规范,依靠scheme,它可以调起iOS App和Android App,不仅如此,跨平台页面调度变得更加灵活、跨平台数据传递也带来很多便利。URD在整个架构中,页面调度解耦,灵活,扩展性好。在运营和推广方面,也变得更加灵活。

Webox的调起流程
至于URD交互流程较为复杂,也不是本篇文章的篇幅所能讲述的,在这里我分享下Webox调起流程,请看图。

###DEK升级###
这一节也是比较复杂,我分享实现的基本原理。在使用上没这么复杂,每个webapp都有一个ID,ID为0时,全量更新(包含所有webapp),ID非0,增量更新,针对webapp的更新由webapp主入口的manifest决定如何更新即可。

使用

客户端会向server请求所需要更新的webapp列表,server返回所需更新的webapp数组

例如 [{id=0, manifest="webapp入口manifest"}]

client根据id判断是全量更新还是只更新某webapp,此时的全量指的是包含所有的webapp

原理

DEK更新机制(采用manifest文件的格式)遵循html5离线存储规范. H5中只是对里面的注释功能进行了扩展,native对Html5离线存储规范的重新实现及扩展。

格式:# [名称]: [值] 即 井号+空格+名称+冒号+空格+值

  • 在H5的模版化架构设计中,每个模块都形成独立的webapp(框架也是webapp,每个模块运行在框架内),每个webapp可独立更新(以dek文件体现)
  • 每个webapp又可拆分为多个DEK(当然一个DEK也可以),便于增量更新
  • 每个webapp都有一个入口manifest,每一个dek会对应一个manifest
  • 可实现全量更新(所有的webapp)和增量更新(单个webapp)
  • 文件级更新:若只更新webapp,则cache字段或network字段决定dek包中的文件更新方式,若cache字段为空,则说明该webapp全量更新(此时的全量是,某个webapp的全量)

Manifest文件格式

CACHE MANIFEST

# Version: 1.0.0.1
# RequiredVersion: 1.0.0
# List: ./others/cache.manifest, is/xx.manifest, ../../xx/xx.manifest
# Dek: xx.dek

CACHE:
images/1.0/bg.jpg
musics/1.3/bg.mp3
css/xx.1.2.min.css
xx.1.1.min.js

NETWORK:
*

格式说明

构建体系

看图!这是个webapp构建部署的结构图。

总结

以URD驱动Webox的客户端平台化架构思想在实际的实践中,较为灵活,可操作性强,对团队具有指导性作用。