2015年上半年,Pinterest的工程师进行了一次实验,借此将移动Web首页的页面加载性能提升了60%,同时移动注册转化率提升了40%。然而该实验使用了一种极为烦琐的解决方案,用到了大量“抄近道”的方法,例如提供预先生成的HTML页面,而没有使用内部模版渲染引擎或其他通用资源(JS、CSS)。为了将实验学到的经验实用化,整个前端引擎、所有页面模版,以及通用元素都必须重写。这是一项繁重的工作,为此我们首先需要构建一个强壮的指标,对整个系统各方面的实现进度进行追踪。本文中我们将介绍提高Pinterest页面性能的具体方法,以及这种方法如何在2016年帮助我们实现了用户数量的最大化增长。
衡量
首先我们希望明确定义并实现我们希望改进的指标。2015年的实验中使用的指标只是从整体上衡量页面加载时间(PLT),这是指自从用户输入URL或点击一个URL后,整个页面渲染完成所需的时间。在浏览器导航时限API方面,navigationStart和domComplete事件之间是存在时间差的:
navigationStart事件是由用户点击链接或在浏览器导航栏输入URL并按下回车后发起的。
domComplete事件是指页面上所有元素的处理工作均已完成,所有资源(例如图片、CSS、JS)均已完成下载。此时用户将不再看到浏览器标签页上旋转的图标,而页面上需要显示的其他新的内容(例如onLoad
javascript)将在此时开始执行。
Copyright ? 17 December 2012 World Wide Web Consortium,
(MIT, ERCIM, Keio, Beihang)
这个指标是个很好的起点,然而有一个重大的不足:无法反映出真实性能。这一点对用户很重要,因为页面上可见部分的加载速度要远远快于整个页面的加载速度。
用户察觉到的等待时间
为了解决这个问题,我们引入了另一个指标:用户察觉到的等待时间(UPWT),这是指从用户输入URL或点击URL后,页面上对用户可见的部分渲染完成所需的时间。这是一种基于图片加载事件的自定义指标。我们会追踪屏幕上包含的图片,以及这些图片完成加载的时间。UPWT始于navigationStart事件,终于domLoading和domComplete事件之间的某一刻:
domLoading事件是指浏览器接收到整个文档并开始渲染的时候。
Copyright ? 17 December 2012 World Wide Web Consortium,
(MIT, ERCIM, Keio, Beihang)
作为一种额外的收益,还可以为移动应用程序引入类似的指标,并用相同的方式进行衡量。
我们将这两个指标(整体PLT和UPWT)以及其他一些性能指标(例如服务器端性能,以及更详细的浏览器端性能)结合在一起,包含在公司最重要的仪表板和我们的实验框架中。借此可以追踪进度并快速了解哪些改进可以实现最大的收获。
经验:创建追踪进度所需的正确指标,优先侧重于可以获得最大改进的指标。
优化
可优化的领域分为三个主要类别:前端、网络,以及后端。
前端
页面重量(CSS/JS/Images/HTML)
通过查看汇总后的测试结果,我们很快意识到页面加载需要极大的带宽。对于某些网络基础设施老旧的国际市场,这个问题尤为严重。为此我们对需要加载的内容进行了更细致的划分。以前我们通常会直接获取整个站点所需的CSS和JS,现在,我们只获取渲染屏幕上内容所必需的CSS和JS,并在初始渲染完成后再延迟加载其他资源。此外我们还研究了所请求的图片,并研究了这些图片是否都是必须的,以及能否请求尺寸经过了优化的图片。这两项措施配合使用后,展示一个页面所需下载的数据量减少了60%。
渲染(React)
在关注性能的同时,我们还将网站端从自行开发的框架迁移到React。我们团队是Pinterest内部较早采用React的,这个渲染模型还让我们进一步大幅改善了性能。通过使用React框架我们获得了大量收益,从一种不受控制,任何东西都可以修改DOM的模式,转变为React的影子DOM批量更新模式。
提前进行Flush/chunked传输编码
为了优化客户端和服务器之间的路径,我们调查了服务器端的页面渲染方式。借此可消除不必要的缓冲,确保浏览器可以提前收到页面的部分,并立刻开始在获取数据的同时,并行获取框架级的JS和CSS资源以及进行服务器端的渲染。在渲染完成后,我们已经开始使用Chunked传输编码方式发送页面内容,但在仔细检查过渲染页面的服务以及最终用户之间的基础架构后,我们发现其中有好几个步骤对响应进行了缓冲,而非直接流式传输。取消缓冲后数据可以更快速到达浏览器,同时页面加载时间进一步获得了改进。
传输
CDN/DSA
我们还对传输基础架构进行了大量改进。我们在CDN中设置了多层缓存,启用了IPv6,切换至CDN的更高服务层,同时在全球范围内引入了SSL边缘终结(DSA)。
后端
尽可能并行处理
页面的渲染通常需要从不同来源请求大量数据。对我们来说,则是需要进行多次API调用。这些调用之间存在一种很自然的数据依赖性图表,借此可以知道哪些调用可以并行处理,哪些因为数据之间的依赖性必须按顺序处理。我们开始考虑使用GraphQL,该技术可以自动优化数据的并行获取。与此同时,我们还对当前使用的调用图表进行细致的审查,以确保所有对顺序无要求的调用都已并行处理。
只返回需要的内容
我们还对所请求的数据进行了修剪,只返回界面所必须的数据。这样不仅可以降低网络负担,而且避免了服务器端不必要的数据获取操作,因为额外的内容通常需要对后端服务进行额外的调用。
尽可能缓存一切
我们还花了一些时间将使用数据“边缘”缓存的页面类型扩展到低基数(Cardinality)页面(例如页面数量约为几百上千,而非数十亿的页面)。缓存方面还有待进一步完善,从考虑到页面数量太多,为了顾及缓存的效率而只缓存页面的“头部”数据到触发缓存在后台自动刷新等,还有很多方面有待改善。
通过改善性能实现用户数量的最大化增长
在为了改善性能而重写页面的时候,绝对不能考虑尝试新的设计。如果用其他更快速的页面设计和最初的页面进行比较,就无法知道转化率的变化到底是来自性能的改进还是设计方面的改进。我们需要构建完全相同的页面来对比。此外为了充分理解对网页性能的影响,整个实验在设置上应该能衡量不同类型页面的指标,以及分别衡量Web和移动Web的指标。随着性能的改善,不同页面会实现不同的转化率和流量收益。对我们来说,将所有页面汇总在一起查看整体转化率是不够的,我们希望能分别查看不同的转化率,随后发现桌面端Web转化率增加了很多,但同时移动Web转化率实际降低了,平均值其实是降低的。我们进一步研究了为什么移动Web转化率降低,并发现在功能方面存在一些问题。
为了让整体页面改进幅度最大化,还需要非常注意,就算与转化率有关的微不足道的功能也需要重新实现。我们最初的页面包含大量此类功能,随着不断地发现并解决问题,我们的转化率开始持续增长。这里学到的最重要的经验是,按照页面类型以及Web/移动Web对页面进行划分,借此更好地理解收益到底来自何处,并更清楚地发现不同划分中可能存在的问题。如果作为整体查看汇总后的转化率变化,这些问题可能会被遮掩起来。
有关转化率的功能清单
完全相同的向上销售(Upsell)机制
导航机制(弹出菜单?新标签?)
注册和表单机制(字段验证信息、相同的字段和步骤)
自动身份验证功能
移动Web和平板App的App向上销售
移动Web深度链接(Deeplinking)
性能重写过程中另一个重要的事情是对每个页面类型进行SEO实验。若想了解有关SEO实验基本功的详细信息,请查阅我们以前发布的博客文章通过实验认识SEO。SEO实验可以告诉我们页面加载时间的改进是否真的能从搜索引擎带来更多流量,在我们的实验中,结论是肯定的。如果你的页面流量很大,也许可以通过实现各类功能改善搜索引擎的评级。SEO实验还可以告诉我们某些功能的实现是否存在问题。就算一些很小的细节,例如图片尺寸或所用的HTML标签也会对此产生影响,因此一定要对所有页面类型进行必要的监视。我们用了几周时间找出并修复了这些问题,SEO流量有了很大提升。
有关SEO的功能清单
重要的标签(例如、hreflang、rel=canonical)
完全相同的图片尺寸
描述性文字
首次页面加载的内容数量
重要经验
尽可能构建完全相同的页面,不要重新设计页面
针对不同类型页面分别进行实验,并区分对待Web和移动Web
同时别忘进行SEO实验
针对划分的不同类型查看是否缺少某些功能,导致降低转化率或SEO效果
修复一个微小的转化率功能让转化率指标有了大幅提升
结果和未来计划
为了改善性能而重建页面的做法让我们用户的等待时间缩短了40%,SEO流量增加了15%,注册转化率增加了15%。由于流量和转化率之间存在倍增关系,因此对我们来说,在Web和应用注册方面这是一个不菲的成绩。这是我们在2016年赢得用户过程中获得的最大成果。此外我们网速慢的用户也能获得更好的体验。多亏了这个项目,我们的团队现在可以更自信地通过改善性能实现更大程度的用户数增长。
|