写点什么

QQ 会员基于 hybrid 的高质量 H5 架构实践

  • 2019-08-23
  • 本文字数:5540 字

    阅读完需:约 18 分钟

QQ会员基于hybrid的高质量H5架构实践

任何技术优化都依托于业务的发展,随着 QQ 会员增值业务的重心转移到手 Q 移动端,对 H5 页面不仅要求加载更快,还需承载丰富多彩的运营活动,同时由于每个页面都意味着 KPI 收入,任何可能导致页面功能不可用的发布行为都是不可接受的。

本文为 SDCC 2016(杭州站)的分享实录,介绍 QQ 会员的前端开发团队在手 Q 的 hybrid 模式下对 H5 页面的性能优化、组件化和持续集成方面的实践。


首先简单介绍一下自己,作为一名 80 后老腊肉,呆过若干创业团队;2012 年加入腾讯超级 QQ 团队,负责前端开发工作;2013 年公司内部组织架构调整加入 QQ 会员团队。


随着移动化浪潮的来临,QQ 增值业务的重心从 PC 端 PCQQ 转移到移动端手机 QQ;作为前端开发,我们的工作内容也转移到基于手机 QQ 的 hybrid 模式下 H5 开发工作。



什么是 QQ 会员?QQ 会员是宇宙第一大包月业务,大部分的会员用户都很年轻。大家可以猜一下哪个年龄段的 QQ 会员用户最多?小学、初中、高中、大学还是白领?所以如果你还不是 QQ 会员,说明你已经老了 :)


个性张扬是年轻的代名词,QQ 会员用户在好友列表中的名字是红色,而且排名靠前,这些都达成了用户的炫耀心理,就连发红包时都拥有右图中的专属的皮肤。


同时 QQ 会员还有左图中的 QQ 等级加速更快等特权,买电影票、定外卖还能打折。


其实手机 QQ 在承担即时通讯等社交功能的同时,还承载着 QQ 会员数十亿的营收重任,其中大部分的营收都来自于内嵌在手 Q 中的 H5 页面,因此保证 H5 页面的高质量,是我们的工作重点。



保证 H5 页面的高质量,我们有以下三个挑战:


第一、如何让 H5 打开更快?雅虎的一项研究表明,页面打开每慢 400 毫秒,将带来 5%-9%的用户流失;让页面更快呈现给用户是前端工程师们的不懈追求,在 hybrid 模式下借助于终端的能力我们有了更大的想象空间!


第二、如何让 H5 开发更快?好的产品是运营出来的,沃尔玛每周都有打折,电商有 6.18 和双 11 双 12,同样 QQ 会员也需要有持续的 H5 运营活动以保持用户的活跃和留存,而 H5 组件化是我们提高开发效率的手段。


第三、如何保证 H5 页面持续高质量。手机 QQ 一两个月发布一个版本,但是 H5 页面每天都有发布,随着 H5 逻辑越来越复杂,比如不同身份用户(非会员、会员)在不同时间点(到期前和到期后)进入页面时看到的内容都不一样;如何不依赖成本很高的人工测试来保证 H5 页面的功能持续可用?


首先介绍下我们基于 hybrid 的 sonic 方案是如何实现页面在 1 秒左右打开的。



1、要打开页面,在 PC 端需要先打开一个浏览器(chrome 或者火狐),在 android 或者 IOS 应用中必须先有一个 webview(图中橙色部分);出于性能考虑手 Q 并未在后台常驻一个 webview 进程,所以要打开页面需要先初始化 webview。


2、在之前版本的手 Q 中我们时常可以看到类似左边的白屏,虽然加上了卖萌的文案“别闹,加载是件正经事”让用户感觉萌萌哒,但这掩盖不了曾经 webview 初始化慢的事实。虽然经过几个版本迭代优化,客户端耗时已经大大降低,但是还需要近 900 毫秒。好像距离一秒的目标很近了。。


3、但是 webview 初始化完成后,再调用 loadUrl 接口获取目标 URL 的 HTML 内容并进行渲染(图中蓝色部分);由于我们的 web 层基于 PHP 语言来实现,一个 web 请求需要新建一个子进程去查询若干个后台服务,这里的耗时至少需要 200 毫秒。算一下终端加后台的耗时加起来已经超过一秒了。。虽然没有人能跑的比博尔特更快,但是我们还是有方法来让我们的页面打开更快。



第 1 个优化是把串行改为并行!我们把终端 webview 初始化工作并行为两个线程(图中两个橙色块):webview 主线程处理主要的初始化工作,而登录态获取、业务插件初始化等工作放在 webview 子线程,这样终端的耗时就从之前的两部分的耗时之和变成了两部分耗时的最大值。同样在后台我们也新建了一个 proxy 来代理后台所有服务的查询工作(右侧绿色块),由 proxy 来并行发起对其他后台服务的查询,proxy 的耗时取决于最慢的那个后台服务接口的耗时。


第 2 个优化是网络耗时的优化。电影英雄中有段对白:剑术的最高境界是心中无剑,手中亦无剑。减少网络耗时最有效的优化方法莫过于不进行网络请求,也就是 Cache。


1、虽然浏览器本身有缓存功能,可以通过设置静态文件的缓存时间来减少请求数,但是我们经过数据验证,发现移动端浏览器缓存有时候并不可靠,缓存还未过期也有可能被清掉重新请求。


2、H5 标准中也有一个 localstorage 特性,我们通过扩展 seajs 的缓存插件实现在 localstorage 中缓存 JS 文件,加快了 HTML 依赖的 JS 的加载速度。但是 HTML 本身仍然需要走网络请求。


3、其实手 Q 也实现了一套离线包机制,用来缓存 HTML 和图片、CSS、JS 等文件,但是只能缓存静态不变的内容,比如刚开始介绍 QQ 会员时的会员个性化红包页面就利用了离线包的能力。然而我们的页面有很多用户数据(比如会员身份、会员成长值、QQ 等级成长速度等)需要实时查询,再加上终端复杂的离线包校验机制耗时很多,我们新建了 HTML Cache 机制,在终端缓存了整个 HTML。


4、有了缓存之后,webview 主线程先发起 1.1 的 loadUrl 操作展示本地 HTML 缓存给用户,同时发起 1.2 的 HTTP 请求去获取最新的数据内容,如果有变更则通过第 3 步的 jsbridge 回调进行页面刷新,同时终端会异步进行第 4 步的更新本地的 HTML Cache。


5、如果页面没有变化,网络耗时仅为加载本地 HTML 文件的 IO 时间,这个时间几乎为 0;如果页面有变化,由于这里提前并行发起了 http 请求,网络耗时也比上一页中串行的 HTTP 直连要少很多。


6、这里还有一个问题,就是如果缓存的 HTML 内容和最新的内容不一致,我们需要刷新整个页面吗?答案是否定。大家注意下这里第 2 步返回内容可能是 HTML,也有可能是 JSON,下一页会介绍为什么。



1、我们将 HTML 拆分为两部分:模板和数据块。一个数据块对应一段 HTML 片段(上图中蓝色字部分),用注释语句包裹起来;而数据块以外的部分为模板,一般情况模板的内容比较固定,dom 结构、内联的样式等很少变动。


2、比如图中有三个数据块:key1,key2 和 key3,分别对应这个页面从上到下三个红框框住的部分。


3、刚才有讲到并行 HTTP 请求回来的内容可能是 HTML,也可能是 JSON;我们的策略是如果是首次访问本地没有缓存或者缓存被清理则返回完整的 HTML;如果模板未变化只是数据块有变化,比如总成长值加了 2 点,从 76660 加到 76662,或者生活福利模块更换了 2 个广告位,只需要返回 JSON 即可,由 jsbridge 触发页面回调来替换 DOM 节点实现页面的局部刷新。


3、以上两个优化点需要终端和页面按照统一规则紧密配合,我们通过扩展 HTTP 协议来实现。



1、我们扩展了 4 个 HTTP 协议头,2 个请求头和 2 个返回头。


2、accept-diff 表明终端是否支持增量更新的能力,一般传 true,对于老版本的手 Q,无法携带该头部,后台将会始终返回完整的 HTML;template-tag 代表终端本地缓存的 HTML 的 SHA1 摘要值;


3、template-change 代表服务端模板是否有变更,模板和数据块均无变更返回 304,模板无变更仅部分数据块有变更时为 false,首次和模板变更时都是 true;cache-offline 是后台告诉终端如何进行页面刷新和本地 HTML 缓存更新,如果为 true 代表刷新页面并更新缓存,如果为 store,代表仅更新缓存不刷新页面。



下面我们从整个流程上来看一下。


第一种场景是用户首次或者缓存失效时加载页面,用户点击终端入口后,在初始化 webview 的同时并行发起 http 链接,在 webview 初始化好之后会在内核和 http 流之间建立桥接。内核在读取完毕之后终端根据模板数据拆分规则对 html 进行内容分割,并记录模板和数据的 tags 信息,异步 HTML 为模板和数据用于下次与服务器通信实时更新。



1、第二种场景是用户二次进入页面,这种情况的占比七成以上。webview 优先加载 HTML 缓存,并且根据 http(s)返回码的同步状态,进行不同的处理。


2、如果 status 为 200,且返回的是 JSON,说明只有数据变更,终端会对数据进行 diff 处理,和页面通过 js 通信进行局部刷新。


3、如果发生模板变更,处理逻辑会有点复杂,终端根据在不同机型和网络环境下做智能切换处理,速度较快时会拉取完 HTML 流交给内核渲染,速度不快时仍然会建立桥接流,并且也会对 HTML 进行拆分;


4、如果 status 为 304 说明完全命中缓存,则不作任何处理;



1、左边的效果是最初页面局部刷新时的表现,我们可以看到加载本地缓存的 HTML 后很快看到了整个页面,然后成长值发生了变动,然后又更新了两个广告运营位。但是这里的体验还是有点问题的,加载图片需要时间,导致页面的闪动很明显。


2、我们又改进了下,先将图片下载完,再去局部更新这两个广告运营位,最终实现了右边比较平滑的效果。


另外一个图片的优化是图片自适应。


网页中的流量大头是图片,图片加载消耗了很多时间。我们实现了对于同一张图片,终端看一根据用户不同的手机分辨率返回不同规格的图片,而这一切不需要做任何代码修改,完全透明接入。


比如如果你是 iPhone 7S,CDN 返回 750 像素的高清大图,如果你还在用 iPhone 4S,CDN 返回 480 像素的一般清晰度的小图,这样在保证体验的同时减少了加载的图片大小,页面更快展现给用户。



这个项目内部代号 sonic,意思是希望页面加载速度可以像音速一样快。最终我们也实现了占比 70%右侧 2 个场景,局部刷新和完全 cache 时总耗时 1 秒左右,而且首次访问时的总耗时也低于之前最左边的 HTTP 直连。



我们除了让 H5 页面加载更快,还需要让 H5 页面开发更快以满足活动运营的需求。



首先我们看一下什么是运营活动?


1、左边第一个活动新游戏即将发布,在预约页面提前预约的用户在游戏发布后下载完成后可以免费领取福利;


2、左边第三个活动,QQ 会员可以免费领取一张美团的优惠券;


4、最右边的活动,QQ 会员玩天天酷跑游戏可以免费抽奖获取游戏道具;



1、运营活动有四个要求:一般 1-2 天需要完成开发测试和上线、不同活动可能有相同的功能逻辑,一般会投入大量推广资源所以对页面的质量要求比较高,大量资源推广时并发访问用户多对性能要求比较高。


2、我们的思路是必须尽可能减少开发环节和开发人力,最小化功能逻辑实现颗粒化可复用,对前端代码和后端服务要求稳定可靠,必须持续的前端性能优化。


3、我们的解决方案是构建一个组件化的活动开发平台,内部代号 ET



1、第一:减少一切可以减少的环节。一般 H5 页面的开发流程是交互-设计-重构-开发,我们和交互、设计人员制定好运营活动的交互设计规范,比如统一弹窗样式,从而减少了交互环节;利用 H5 的新特性 canvas 自动对设计稿进行切图,又省掉了重构环节。


2、第二:组件化开发。开发人员只需要开发组件,组件可以在不同活动中复用。运营人员只需要拖拽组件、配置资源,最后由执行引擎生成包含活动逻辑的 HTML 页面,自动发布外网即可。



一个组件由 HTML 片段,CSS 样式和 JS 逻辑构成;开发人员完成组件开发之后,运营人员像拼积木一样,拖动几个组件组合在一起,就可以生成运营活动页面。


同时 ET 平台实现了一整套发布回滚流程支持,自动对接页面性能测试工具,可以对运营页面的性能进行自动化测试,最后也会给大家分享下如何进行性能自动化测试的。



该平台上线后,月均上线活动达到 300 个以上,但全职开发人员投入仅 1 人。



保证 H5 页面功能正常,并且让 H5 页面打开更快,不是一锤子买卖,需要可持续。H5 页面的质量不能仅仅靠测试人员的手工测试来保证,我们需要一套自动化解决方案。



1、说到质量标准,IOS9001 是我们耳熟能详的国际质量标准,但是 H5 页面的质量标准是什么?


2、PC 时代,我们知道 performance api 就能比较全面的透视整个页面请求过程的耗时,在 hybrid 模式下,我们对 H5 页面高质量的定义是页面功能的高可用和页面加载速度更快。



3、功能高可用需要 webview 不会 crash,页面能够正常打开并且业务逻辑符合预期;页面加载速度更细化,终端耗时、网络耗时、页面耗时,同时需要关注总耗时大于 5 秒以上的慢用户占比。



1、页面功能可用性的自动化测试,我们构建于腾讯内部自研的自动化测试工具 QTA。该工具不仅可以识别 android 和 ios 终端的控件,也可以识别 web 的 dom 控件,通过对点击事件进行模拟,将实际的返回值同期望值比较以确认用例是否通过。


2、测试人员使用 python 语言编写自动化测试脚本上传到 SVN,由分布式任务管理系统分配可供测试的手机模拟器或真实的手机,测试人员可以手工或者设置定时任务自动执行测试计划。


3、同时我们将 web 发布系统和任务管理系统进行打通,每次发布前自动进行功能自动化测试,只有在预发布环境的通过率达标才能继续发布,这样就保证了频繁变更时 H5 页面的功能依然正常。



1、页面性能自动化测试我们参考了很多现有的工具,比如 yslow,雅虎前端优化军规以及谷歌的 pagespeed,但是发现这些对 hybrid 模式支持的都不是很好,尤其是我们基于手 Q 环境下有更多的个性化的东西。


2、我们选择了自研 H5 页面性能自动化测试工具,简称为 WPT,web performance test。参考了 yahoo 军规,结合终端环境特性和 H5 业务特性,对 H5 页面加载的全流程进行发布前测试和发布后回归。



简单回顾下,我们通过 H5 页面和终端的深度融合实现了 H5 页面的快速加载,同时通过组件化实现了 H5 页面的快速开发,使用自动化工具实现了 H5 页面变更时的持续的高可用和高性能,最终实现了高质量的 H5 的架构实践。




作者介绍:


翟伟,QQ 个性化业务前端团队 leader,曾参与过超级 QQ 的和 QQ 会员的前端开发工作,目前负责手机 QQ 个性装扮的开发工作,拥有近 10 年的项目架构和实践经验,专注于手机 QQ 的 hybrid 模式下 H5 优化和持续集成方向。


本文转载自公众号小时光茶舍(ID:gh_7322a0f167b5)。


原文链接:


https://mp.weixin.qq.com/s/Pz2d81-OUfB_LQuO5vf1zg


2019-08-23 16:2410747

评论

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

如何优雅的用Redis作为Mybatis的二级缓存?

做梦都在改BUG

Java redis 缓存 mybatis

软件测试 | 参数化job

测吧(北京)科技有限公司

测试

软件测试 | Jenkins报警机制

测吧(北京)科技有限公司

测试

软件测试 | 单元测试体系集成

测吧(北京)科技有限公司

测试

Python 基础练习题(六)

漫步桔田

软件测试 |静态扫描体系集成

测吧(北京)科技有限公司

测试

三天吃透Java虚拟机面试八股文

程序员大彬

Java JVM

React组件之间的通信方式总结(下)

beifeng1996

前端 React

今天终于知道 Redis 为什么要用跳跃表了

做梦都在改BUG

Java 数据库 redis 缓存 跳跃表

软件测试 | HTTP网络认证体系

测吧(北京)科技有限公司

测试

软件测试 | 接口加密与解密

测吧(北京)科技有限公司

测试

软件测试 |多套测试环境下的接口测试

测吧(北京)科技有限公司

测试

软件测试 | Jenkins权限控制

测吧(北京)科技有限公司

测试

IO:阻塞和非阻塞、同步和异步

小小怪下士

Java 程序员 io 异步 阻塞

刚插上网线,电脑怎么知道自己的IP是什么?

做梦都在改BUG

Python 基础练习题(四)

漫步桔田

软件测试 | Jenkins持续集成

测吧(北京)科技有限公司

测试

新时代的技术领导力

agnostic

领导力

软件测试 |Jenkins的常用插件

测吧(北京)科技有限公司

测试

React组件之间的通信方式总结(上)

beifeng1996

前端 React

社招前端常见react面试题(必备)

beifeng1996

前端 React

前端常考react面试题(持续更新中)

beifeng1996

前端 React

JavaScript 为什么要进行变量提升,它导致了什么问题?

Immerse

JavaScript 面试 js 前端面试题 超全前端面试题

仅一小时点赞破万!GitHub爆赞的Spring Boot最佳实践

做梦都在改BUG

Java spring 微服务 Spring Boot 框架

RocketMQ 消息重试机制、死信队列

做梦都在改BUG

Java RocketMQ 消息队列 消息中间件

GitHub标星百万的Java进阶架构师手册,用20%技术解决80%问题?

做梦都在改BUG

Java 架构师

软件测试 | Jenkins job机制

测吧(北京)科技有限公司

测试

Python 数学练习题(二)

漫步桔田

软件测试 | Jenkins的节点管理

测吧(北京)科技有限公司

测试

Python 基础练习题(五)

漫步桔田

软件测试 | 矩阵job与父子job

测吧(北京)科技有限公司

测试

QQ会员基于hybrid的高质量H5架构实践_架构_翟伟_InfoQ精选文章