王向维,京东商城三级列表页架构师,完成列表页的nodejs版本到nginx+lua版本的变迁,并做了大量三级列表页的服务端和前端的优化工作。
在持续开发一个核心系统过程中,除了满足业务需求外,还应该考虑系统未来的架构,追求极致的系统的可用性、高性能和稳定性。这个过程是一个长期积累和重构的过程。
每个应用都要满足自己特定的需求,因为商业条件、应用场景、用户期望,以及功能复杂性各不相同。尽管如此,如果应用必须对用户作出响应,那我们就必须从用户角度来考虑可感知的处理时间这个常量。事实上,虽然生活节奏越来越快——至少我们感觉如此,但人类的感知和反应时间则一直都没有变过:
时间
感觉
0~ms
很快
~ms
有一点点慢
~0ms
机械在工作呢
0ms
先干点别的吧
00ms
不能用了
这个表格解释了Web性能社区总结的经验法则:必须50ms内渲染页面,或者至少提供视觉反馈,才能保证用户不走开。如果想让人感觉很快,就必须在几百ms内响应用户操作。超过1s,用户的预期流程就会中断,心思就会向其他任务转移,而超过10s,除非你有反馈,否则用户基本上就会终止任务!
下面将从前端、服务器端、缓存、兜底等来说说如何优化京东三级列表页。
前端优化
京东三级列表页从优化到上线,已经经历了两个和一个双11的考验,每天有上亿的访问量,页面打开时间在0-80毫秒(在某些地区或低带宽下会大于ms)。
优化四原则
精简和瘦身页面,首屏优先展示出来;
需用户交互的部分惰性加载;
能不执行的先别执行,惰性执行;
滚屏惰性加载。
一、HTML文档要精简
目的:尽快渲染出页面并达到可交互的状态。
方法:
1、如果非必须,尽量只生成首屏需要的html数据;
、优先获取资源、提前解析。如首屏需要的css和js;如果不考虑维护成本,可以把首屏需要的css和js放到文档中;
3、发现和优先安排关键网络资源,尽早分派请求并取得页面;
4、文档精简后,服务端生成程序耗时短,性能才会好。
如列表页的头、面包屑、品牌区、属性筛选区、60个商品主图数据,这些是服务端模板渲染输出;而剩余部分是在前端JS惰性加载或生成。
二、需用户交互的部分惰性加载
对于三级列表页品牌区,服务端只渲染18个品牌,用户在点更多时,ajax异步加载其他的。对于整个属性是筛选区服务端只渲染5行,其他行用户在点更多时,js从文档嵌入资源中取到数据,并渲染成html。这样做可以保证服务端计算少,提升服务端性能,减少数据传输。如下图点“更多”时才加载更多的品牌,因为有些三级类目有非常多品牌,如果不采用这种方式,整个页面渲染非常慢。
因为需要SEO的原因,京东三级列表页不能使用bigpipe等技术来进行更优的处理。
三、能不执行的先别执行,惰性执行
上图是三级列表页最重要的商品区(商品主图+N个关联商品小图),每个商品的区域都是完全一样的;如果在服务端拼装整个商品区域的话,尤其涉及到小图部分,会有非常多的重复html元素;我们把体验和减少页面内容进行了折中处理;服务端渲染输出商品主图部分;小图部分通过json数据嵌入到页面,然后通过js惰性执行渲染。这样可以很好地对页面进行瘦身。而且小图资源是页面嵌入的,非异步加载;没有网络请求,用户基本感知不到异步带来的渲染闪动问题。下图就是页面嵌入的小图json数据。
四、滚屏惰性加载
三级列表页的60个商品区域的图片和页尾都是当用户向下滚动页面时,才去加载当前屏幕中的图片和模块。这样可以节省服务器带宽和压力,提升页面整体渲染时间。
上边就介绍完了三级列表页在优化时使用的最主要的四个原则,而实际优化过程中,还涉及到非常多的优化细节,如下部分将介绍这些细节。
将一些JS/CSS资源直接嵌入页面
把资源嵌入文档可以减少请求的次数。比如页面需要的js、css数据。如下图所示:
上图中的这些js对象,是后端渲染输出的,因此不适合放入单独的js文件,直接在页面中嵌入输出会更好些。slaveWareList是小图的列表对象。如果放在服务端模板渲染输出的话,首先需要进行一些循环拼装页面;另外会使页面体积变得非常大。权衡之后决定放到前端js渲染输出。这样也带来了一些好处:减轻服务端压力,提升渲染模板性能和减少服务端执行时间;服务端不用生成html,文档减少上百个div,减少页面大小和网络开销;提前放到文档中,不用异步调用;用户基本感知不到渲染过程。
对引入的资源排定优先次序
根据自己系统的业务,对每种资源定优先级:对必需的资源优先加载,而低优先级的请求保存在队列中延时加载或等待必需资源加载完再加载;如:搜索推荐热词、顶部三个热卖商品接口、60个主商品的图片、价格优先加载。而对于库存、促销信息、广告词、预售商品、店铺信息等,延后加载。对于点击流,广告统计数据则延时两秒再加载。
应用js缓存来存储公有属性和商品信息属性
三级列表页中的每个商品都是一个对象,存放在一个map中,通过ajax接口异步填充和维护商品的属性。用于后续用户交互用。同时维护成本也会降低;即页面中用到的每个商品数据放入一个map中,如果没有则异步加载;如果有直接使用;即这些数据是公共数据。
Ajax接口最优调用
页面往往依赖很多的异步接口,因此要对异步接口进行压测,找出接口的最优调用方式。如京东三级列表页依赖价格、库存、广告词、店铺信息等异步调用接口。而页面有时候会出现多达多个商品,如果用一个get请求把这些sku做参数,性能非常慢,那么就要采用分组分批调用。如页面商品在个时,价格接口分六组,第一组30个,第二组30个,第三组60个,第四组60个,第五组个,第六组个。
DNS预解析
对可能的域名进行提前解析,避免将来HTTP请求时的DNS延迟。如对价格、库存、图片、单品页等服务预解析。
减少HTTP重定向
HTTP重定向极费时间,特别是不同域名之间的重定向,更加费时;这里面既有额外的DNS查询、TCP握手,还有其他延迟。最佳的重定向次数为零。比如三级列表页以前是北京正规看白癜风医院治白癜风合肥哪家医院好
转载请注明:http://www.qianzhongdushijian.com/jdzy/1727.html