有货移动Web端性能优化探索实践

发表于:2018-2-28 10:04

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:周奇琪    来源:前端之巅

  在移动互联网的时代里,对于一个 web 站点来说,移动端的用户体验尤为重要。现代 web 站点的设计和开发都是以移动优先作为第一原则,我们也专门为了移动端的 web 站点做了相应的优化和提升。而网页的打开速度和页面的流畅度,对于用户是否长时间访问至关重要。我们在移动端的站点通过一系列的方法,最终为了快速打开页面展示网页内容,触达用户,同时能流畅的浏览网页。
  移动端的硬件条件,网络条件相对于桌面端,会复杂的多,设备类型多样,硬件配置参差不齐,分辨率碎片化,网络状况在移动过程中稳定性,速率都会变化,而对于一个页面到达用户的终端展示,会经过,用户发起请求,服务端接受请求,服务端处理请求,返回响应内容,在用户终端的浏览器展示内容,用户操作页面发起其他页面时间,而这个过程中任何一个环节的延迟都会造成性能瓶颈,降低用户继续访问的可能性,所以我们在服务器端,浏览器端,网络加载,多个方面做了一系列的优化工作。
  WEB 服务端优化
  有货的 WEB 端主要使用了 nodejs,基于后端服务提供的 HTTP 接口服务来实现的前后端分离,这里的服务端优化主要是指在 nodejs 实现的 web 服务端进行优化。
  前后端调用结构
  优化的目的是提升服务端的响应和并发能力,充分发挥 nodejs 的异步非阻塞的特性,主要从以下几个方面去优化。
  接口服务调用的优化
  对于一个页面展示的路由,要处理这个路由,可能需要调用多个接口并且进行进行界面逻辑的处理。大体过程:
  ●接口合并 我们对于一个页面调用可以合并的接口,进行接口合并,减少接口调用次数,如:以商品详情页为例,商品的一些特性,可以在一个接口返回,尽可能的减少接口调用的个数,因为每次接口的处理都有网络 IO, 对象序列化,压缩和解压的过程。
  ●接口异步调用 但是并不是所有的接口都可以合并,对于无法合并的接口,我们尽量使用 node 的异步非阻塞的特性,进行异步调用,同时调取多个接口,而最终的调用耗时取决于最慢的接口。
  这里要说明一点:对于接口依赖,如 A 接口依赖 B 接口的返回结果,像这种情况,我们最好梳理下接口设计,减少这样的串行调用,因为这样,调用耗时是多个接口耗时的总和。
  ●减少接口交互数据 返回的数据较多的情况下,会导致 JSON 序列化,数据批量对象处理,产生额外的性能损耗。可以做下接口返回数据结构的精简,返回必要的字段(页面会展示用到的数据)以及可以调整返回 item 个数。从而达到减少数据的返回消息体的大小。此外请求接口时需要 gzip 压缩,可以大大的减少网络传输的时间,尽管需要解压会消耗一部分 CPU 的时间,但是对接网络 IO 的损耗,还是值得的。
  可以分享一组基准压测数据,在调用接口使用 gzip 和不使用 gzip 的 QPS 数据
  ●减少接口调用次数 如何减少接口调用次数呢?对于一个页面,可能就会存在调用必要的接口,在这里我们使用到了缓存机制,对于热数据进行接口缓存,我们使用了一些内存数据库,同时对于一些规格数据可以进行进程级的缓存(如:导航信息,品类信息等)。缓存是有一定原则的:第一,需要容易命中的数据,第二,可以被缓存的数据,数据更新频率是可控的。通过缓存机制,一部分接口调用就会走到缓存,减少的接口调用的 IO。此外缓存的数据可以是对接口数据处理后的视图对象,同时也减少的数据处理的时间。
  ●内部服务调用 DNS 缓存 我们的内部服务使用域名方式,为了提高服务的灵活配置,但是需要内部 DNS 服务器进行域名解析,这个是有一定耗时的,所以我们在 DNS 这块加了 DNS 应用端 cache,减少 DNS 解析的时间。
  业务处理的优化
  现在我们主要的服务端业务处理,主要对于页面逻辑的处理,如路由控制,会话处理,视图对象处理,模板渲染。我们在这些处理过程中进行了一些优化。
  如何发现 node 的性能问题,主要可以使用 cpu-profile 进行 cpu 处理堆栈的抓取,然后使用 chrome 的 dev-tools 进行火焰图的分析,找到性能瓶颈。
  计算密集型操作使用原生实现 js 是不擅长计算密集型的操作,如 Hash 处理,加密解密,压缩解压,像这些操作可以直接使用 nodejs 提供的原生实现(crypto, Zlib)
  以下是一组使用原生和 js 的 md5 处理性能对比:
  返回结果:
  native md5::31.59
  result:5d3b7d53fdd4daaa2d75370e8a5d1789
  js md5::181.54
  result:5d3b7d53fdd4daaa2d75370e8a5d1789
  差距还是比较明显的。
  模板渲染的优化
  我们在实际使用过程中,发现模板的渲染是十分消耗性能的,特别的模板的预处理过程,如果预处理过程是在用户访问过程中去处理,会慢不止一个数量级,所以我们把预处理的过程提前了(改造了 hbs),在启动 web 应用时,已经预编译完成。同时我们发现 handlebars 的一些默认配置属性,如缩减处理,在字符串拼接过程中会损耗一定的性能,所以可以关闭 html 片段的缩减。
  此外,我们还把可以缓存的 html 片段进行进程级的缓存,性能提升显著,可以把一些不怎么会变的 html 公共部分进行缓存。通过内部缓存刷新机制进行定时刷新 html 片段。
  nginx 的优化
  启用 page cache 使用了 nginx 的 proxy_cache 模块,配置了一些缓存机制,不同页面路由的缓存时长会读取 node 服务在 http 头里面返回的 max-age 时间。
  proxy_cache cache_one_wap;
  proxy_cache_valid 200 1m;
  proxy_cache_min_uses 1;
  proxy_cache_key $host$uri$args;
  add_header X-Cache-Status $upstream_cache_status;
  然后我们会在应用服务添加 max-age 配置的中间件, 对路由进行拦截装饰 http header:
  const cachePage = {
      '/': x * MINUTE,
      '/boys': x * MINUTE,
      '/girls': x * MINUTE,
      '/kids': x * MINUTE,
      '/lifestyle': x * MINUTE,
      ...
  }
  另外要注意一个设置 nginx 缓存的时候,如果有服务端设置 cookies 的情况下,并且以服务端 cookies 的值作为标识用户会话信息,不要设置 proxy_ignore_headers "Set-Cookie";,不然缓存会导致会话信息窜读的情况。
  全站 HTTPS
  为什么要上全站 https 呢,这个主要考虑到 https 可以防止中间人攻击以及内容劫持,提高网站的访问安全性。但是因为多了 SSL/TLS 的服务端和浏览器端的处理, 在性能方面也会有相应的下降,但是我们同时也启用 HTTP/2,而主要的特性:多路复用 (Multiplexing) 多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求 - 响应消息。另外 PWA 里面的 service worker 也是必须要求网站的协议是 https 的,同时也是为了这个做了铺垫,HTTPS 是现代 WEB 的发展趋势。至于 PWA 不是本文讨论的重点,可以在后续其他的主题展开。
  浏览器端优化
  移动终端五花八门,导致过重的浏览器的处理和效果,会导致体验的不一致,特别是安卓手机,所以我们在浏览器端的策略是,尽量轻量化网页,当前页面只处理当前必要的内容多页面的方式。这个和现在 google 提出的开源项目 AMP 是一个思路。
  首屏直出优化
  从用户发出请求到页面完全展示,一般来说在网络正常基本上 1s 以上,但是如果页面打开耗时超过 1s,用户流失的概率就会线性上升。所以移动端秒开至关重要,所以我们的思路是减少白屏时间,尽快把浏览器可视区域展示出来,就是所谓的首屏,我们就从以下几个方面做了优化。
  直出文档,简化 dom 结构 服务端进行 HTML 渲染输出,只处理首屏需要的 HTML,并且简化 DOM 的树状结构,如:
  <div class="nav">
     <ul>
        <li>xxxx</li>
        <li>xxxx</li>
     </ul>
  </div>
  可以简化成
     <ul class="nav">
        <li>xxxx</li>
        <li>xxxx</li>
     </ul>
  在页面只保留必要的 DOM,此外,可以估算下世面手机分辨率,确定最大首屏的输出可视区域的 DOM,如果需要滚屏到第二屏的,可以延迟通过 Ajax 获取内容加载。减少 DOM,可以减少 HTML 的输出,当然更重要的浏览器的布局和渲染的时间大大减少。当然首屏的静态资源和样式要优先加载,下个关键点就是首屏只加载所需样式和静态资源。
  首屏只加载所需样式和静态资源 光有 DOM 的处理是远远不够的,要从白屏到展示完整的首屏,还需要样式和静态资源。所以要优先加载样式和静态资源,所以直接把公共样式中首屏用到的样式抽离出来,并且首屏用到的样式,直接在 html 页面内置。此外,图片和字体等其他需要展示的部分,优先加载,促使首页快速展示出来。
  首屏渲染,js 延迟执行
  当首屏渲染的时候,这时候 js 的执行可能会阻塞渲染的线程,所以为了减少对浏览器主线程的渲染过程,尽量延迟进行 js 执行,特别是操作 DOM 的情况,不然首屏展示过程中会产生额外的重布局和重绘,js 引入或代码直接放到页面的底部,在 body 之后,在 html 之前。
  优化直出服务端处理时间 另外再强调下直出的关键,服务端的处理 wait 尽量减少,对于首屏直出至关重要。
  图片优化
  图片质量和体积控制 对于移动终端来说,分辨率相当于桌面会小很多,首先会降低图片的分辨率,以及图片的 DPI 值,第二步会降低图片的质量,以保证图片的体积变小。
  提高 CDN 缓存命中,第一,减少缩略图的尺寸规格,第二,尽量和其他端的图片规格保持一致,如 APP 端,小程序。
  WEBP 的运用 webp 不是所有的浏览器都支持,所以,我们的做法是对于需要 js 加载的图片,进行 webp 的判断,如果支持,就是直接加载 webp 的格式图片,如果不支持,采用默认的 jpg 的格式。
  if (window.supportWebp && (/format\/png/i.test(query) || /format\/jpg/i.test(query))) {
      imgUrl = imgUrl.replace(/format\/png/i, 'format/webp').replace(/format\/jpg/i, 'format/webp');
  }
  webp 加载截图



上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号