现在注册

登录

忘记密码

忘记密码? 请输入您的电子邮件地址。 您将收到一个链接,并将通过电子邮件创建一个新的密码。

淘宝和天猫首页都用到了哪些技巧或者技术?

最近研究如何提高网站首页访问加载速度技术

评论 ( 8 )

  1. 最近忙的事情太多,一直抽不出写新文章的时间,今天暂时还是从我正在写的新书草稿中抽一篇文章来分享给大家读一读,预计需要九月底才能抽出时间正式写文章了。

    在免费流量渠道里面,除了搜索流量外,可能最受大家关注的就是手淘首页流量了。现在手淘首页流量基本上成为了大部分商家的流量必争之地,为了能抢到这块流量,很多商家不惜花上高额的培训费去学习“黑首页”的技术。

    其实,要获取手淘首页流量也不是一件很难的事,,只要你懂得手淘首页猜你喜欢的推荐逻辑,然后根据这个逻辑针对性的优化,你无须所谓的“黑技术”也照样可以轻松获取到手淘首页流量。

    手淘首页猜你喜欢的推荐逻辑:

    猜你喜欢,顾名思义就是给消费者推荐最喜欢和目前最想要购买的优质产品,当然,淘宝肯定不会是毫无根据的乱猜,而是会根据消费者最近的行为习惯和历史购物记录来推理分析,然后再推荐,行为习惯主要是指消费者的浏览行为,加购和收藏的行为,点击行为等等,历史购物记录主要是指消费者的性别,消费水平,个性偏好等等,但是整个过程占比权重最大的是消费者行为习惯,历史购物记录只占比了非常小的一部分权重。

    为了能够给消费者推荐他最喜欢和最想要购买的产品,淘宝系统需要做好两件事情:

    第一件事是推理分析出消费者最近想要购买什么商品,然后再分析出在这个商品里面他会最有可能选择什么风格的,什么类型的,什么价位的等等。

    淘宝是怎么分析得出这一步的呢?

    其实,淘宝主要是看买家最近在淘宝上的购物行为习惯,看看他在搜索一些什么样的关键词,点击了一些什么样的宝贝,加购收藏了一些什么宝贝。所以很多的时候,淘宝在这一块上并不是做的特别精准,例如,我最近因为在分析母婴类目的市场,所以我每天都在搜索和点击母婴类的产品,结果淘宝在我的手淘首页猜你喜欢的位置全部推荐的都是这些母婴类的产品,而且推荐的价位基本上都是我点击过的产品的价位,款式也是点击过的产品的款式。不过他推荐的不一定正好是你点击过的那个产品,而是推荐与这个产品相似的销量更高的,人气和综合能力更高的产品,其实这一推荐法对于新品和做的不好的商家来说,这不是一件好事,因为消费者点击了你的产品,如果当时候没有购买,淘宝立马就把你这个产品的同款或者相似款推荐推荐在猜你喜欢的位置了,而且推荐的都是竞争力比你强,销量比你高的商品。很多消费者可以看到这些竞争力更强的商品以后就不会去购买哪些表现比较差一点的宝贝。

    第二件事是把你的商品打上人群标签,因为他最后是要根据这个人群标签去给你匹配人群,在这一点,淘宝主要是根据你商品的标题,属性,图片文本,以及消费者的反馈来打标。而这里面占比权重最高的其实是消费者的反馈,主要包括点击率,转换率,加购收藏率等指标。

    当然,除了这两点外,其实淘宝在推荐逻辑上还有一些规则性的门槛,例如:对图片就有要求,虽然不一定要求是白底图,但背景一定要干净利落,图片上不能出现太多的商品,文案等牛皮癣,这一点你仔细去看淘宝首页猜你喜欢位置的商品就能看出来,你会发现大部分在猜你喜欢位置的商品基本上都是比较干净的主图。

    当我们了解了这个推荐逻辑后,接下来我们就结合猜你喜欢的流量的推荐逻辑对我们的宝贝进行优化。

    1.满足手淘首页猜你喜欢的基本门槛规则,主要体现在图片干净,图片点击率高,如果这两个指标没达到,基本上很难获取到猜你喜欢的流量

    2.优化好标题和属性,注入流量,帮助淘宝系统对你的商品进行人群打标。从它的推荐逻辑我们已经知道了,淘宝主要是根据商品标题,属性,图片文本,以及消费者的反馈来给商品打标,因此,我们首先需要做好的就是优化标题,属性,主图和详情。然后注入流量,这一点特别重要,如果没有流量的注入,基本上很难触发手淘首页流量,你仔细观察过商品流量渠道趋势的话,你会发现绝对部分情况下,手淘首页流量绝大多数都不是最先进来的,他往往是在大量的直通车流量或者自然搜索流量或者活动流量进来之后一段时间才起来手淘首页流量。因为淘宝给我们打标的过程中有一条非常重要的指标,就是消费者的反馈,他要通过消费者的点击率,转化率,加购收藏,跳失率等指标去判断应该给你打上什么样的标签。如果你流量都没有进来或者进来的太少的时候,淘宝没办法完成它的打标。

    3.做好销量,uv价值,销量增长趋势这一项工作。淘宝在猜你喜欢的位置是按照优质宝贝优先推荐的原则,这也是为什么明明消费者点击了你的商品,淘宝却在手淘首页推荐你的同款而不是你的商品的原因。这就关系到这三个最重要的指标了

    第一个重要的指标就是销量,销量可以说也算是一个基本门槛了,零销量和低销量的宝贝很难出现在猜你喜欢的位置,能出现的基本上都是比较高的销量宝贝,或者销量增长趋势特别好的宝贝,你有时候会发现偶偶会有一些低销量的宝贝出现,但是这些宝贝要么短暂的出现,马上消失,要么就是过几天人家就已经成了高销量的宝贝了。当然,每个类目对销量的要求是不一样,根据我对服装类目的观察,如果你销量突破一千了,每天销量能在一百单以上的时候,获取手淘首页流量的能力会标强。

    第二个重要的指标是Uv价值,你也可以理解成坑位产出。淘宝给了你这么优势的位置,如果你没能有一个好的uv价值,基本上你也很难在这里呆长久

    第三个重要的指标是销量的增长趋势。淘宝会去判断你的商品的增长趋势及潜力的。如果你的增长趋势特别好,淘宝会给你越来越多的手淘首页的流量,例如,你的一个款宝贝今天一百的销量,明天两百销量,后天四百销量,按照这样的增长趋势,只要你满足手淘首页的基本规则,你的流量会来的非常快,可能今天你还只有一万,明天会突然就有十万以上的访客了,但是如果你今天八百销量,明天七百销量,后天只有六百销量了,曾时候你的手淘首页流量也降低的非常快,这也是为什么我们经常发现手淘首页流量来的快,来的突然,但是却的也快,去的也突然的一个原因。这也算是淘宝给商家的一个公平把,如果单纯只是按照销量算的话,那些已经做爆了的款就会长期霸占手淘首页了,淘宝肯定也不容许这事的发生,但是增加销量增长趋势的话就不一样,任何一个款你都很难说你能一直持续的增长下去,总有一天你会停滞或者下降,等到你按个时候淘宝就开始去扶植新的增长趋势的优质宝贝。

    当你把这些方面都做好了的时候,获取这一部分手淘首页的流量也就很轻松了,不过为了能够手淘首页流量持续的时间长一点,可以在你销量增长趋势疲惫的时候通过直通车拉动或者活动的拉动反哺一下,这样一般能持续的时间更长一点,另外,我也发现,在上聚划算,淘抢购这些活动的时候,前一两天和当天淘宝会额外给你增加你的手淘首页流量,而且后面也会持续的更长久一点,当然这个是我自己店铺数据的一些结果,不能百分百代表所有店铺都会这样。

  2. Like this


  3. 点进去 jubao.12377.cn:13225/re 是这样的

    不愧是可以买到核原料的网站,威武霸气

  4. 用了什么技术不重要,重要的是如果支付宝断网了呢

  5. 这篇文章写于2016年4月,已经过去了两年多,业务归属和技术体系都发生了天翻地覆的变化,文中内容已经和线上情况有所出入,因此仅做性能优化参考。

    ———————2016年的分割线—————————-

    作为天猫首页的负责人薛定谔,我也只好解剖一把天猫首页,让大家看看它的骨肉血皮到底是怎个模样。人格担保虽然有撸文狂魔小胡子哥珠玉在前,但本文绝对有不一样的地方哟~
    比如……如何快速响应需求,解放生产力。
    比如……移动端首页和WeeX首页的优化经验。
    比如……广告,天猫前端团队正在诚意招资深前端开发工程师(社招),简历请发到yueyuan.cx@alibaba-inc.com

    《PC与无线齐飞,Web共Native一色——天猫首页全解密》
    从14年实习开始参与PC首页的开发,到15年正式入职同时负责无线端的首页,至今已经两年的时间。期间历经了6次改版、php到Node端的迁移、ReactNative的尝试、无线端性能优化、进入ES6时代、WeeX的尝试等等,不一而足,感触颇多,与大家分享。

    也是一篇难得的长文,先放提纲。

    一、天猫首页的定位
    二、天猫首页的技术架构变迁史
    1. 从PHP到Node的蜕变
    2. 从静到动,从PC到移动端的飞跃
    3. 光影融合,Web与客户端的交汇
    三、疾如风——天猫首页的性能优化
    四、侵略如火——大胆的Native化尝试
    1. ReactNative的初登场
    2. WeeX的回归
    五、不动如山——天猫首页的稳定性保障
    六、小结

    一、天猫首页的定位

    在了解天猫首页的技术架构之前,先让我们问自己一个问题:首页到底是什么?是用户关于这个网站的第一印象吗?还是所有业务的起始点?

    简而言之,天猫首页有着以下三个特点:

    1. 面向最广大消费者的标杆与门面。
    2. 流量的入口与中转站。
    3. 轻耦合,新技术的试验田。

    作为天猫的门面,往往决定了消费者对于天猫的第一印象,所以天猫首页一直在性能优化上有着孜孜不倦的追求,优化加载性能、交互体验、减少流量消耗、Native等多种方式给于消费者更好的体验。

    作为流量的中转站,首页赋予了运营非常多灵活机动的能力,例如节日氛围的营造、机动区块的变化能力,并且全面铺开个性化,能够基于人群、地区推荐更加适合用户的商品,导流效率遥遥领先于其他页面。

    作为新技术试验田,天猫首页一直处于开放的态度,敢打敢冲,率先尝鲜新技术并落地:2014年9月份率先从PHP迁移到Node、天猫内第一个尝试ReactNative、第一个进入ES6时代、第一个落地WeeX的重要业务。在这一过程中,天猫首页往往会克服非常多先行者的困难,并反哺新技术的优化与成熟。

    二、天猫首页的技术架构变迁史

    天猫首页的技术架构变迁,主要是基于两个部分:内部搭建系统&模板渲染系统的变更、业务重点的变更。

    1. 从PHP到Node的蜕变

    PHP时代,主要存在以下问题:

    • 老旧的服务端实时渲染架构。渲染性能差、服务集群庞大使得文件同步生效慢。
    • 旧搭建系统规范使得效率低。需要手动在php文件中挖出供运营填写数据的位置,该数据位难以定义其他校验规范。
    • php文件和css/js文件不属于同一套部署系统,需要手动控制顺序,先发布新版本css/js文件到cdn,再发布php文件更新引用css/js文件的版本,容易出现发布手误。

    Node时代,上述问题都得到了解决,在首页完成迁移Node的尝试后,我们拥有了新的内部搭建系统斑马和模板渲染系统Wormhole:

    • 在使用『Cache集群缓存渲染好的HTML』这一架构后,性能、抗压能力、可扩展性都有了极大的提升(关于这一点详细可以看 @Barret李靖 的回答)。
    • 接入schema规范系统,建立数据位置的代码不再与前端代码杂糅在一起。
    • 隔离职能。HTML模板代码专注于渲染,不再处理其他事情。
    • 部署系统统一。不再出现需要关注文件发布顺序、手动升级版本的情况。

    关于Node应用的更多信息,可以:关注前天猫Node负责人 @死马 的回答Node.js 在双十一中有哪些应用,表现如何? – 死马的回答以及现天猫Node负责人 @ngot 的回答如何看待天猫彻底抛弃PHP? – ngot 的回答

    2. 从静到动,从PC到移动的转身

    从2014年开始,天猫开始集体转型,迎接移动端时代的来临,直到2015年双十一,移动端成交占比已达到68%。

    无线时代有甜也有苦:

    • 不需要纠结坑爹的IE兼容性,但是老版本的安卓兼容起来更加痛苦
    • 因为安卓的碎片化,响应式也是需要考虑的重要问题
    • 高清化的处理方案
    • 部分方案需要优化如iconfont,因为可能会发送多个请求,影响性能
    • 糟糕的网络环境、机器性能、系统特性对于性能有着更加极致的需求

    因为移动端和PC端用户的使用习惯差距较大,同时考虑后续维护的方便性,移动端的天猫首页的UI设计上并未使用传统概念上的响应式(PC和移动端一套代码),而是与天猫客户端保持一致。
    这一前提使得移动端Web首页可以和客户端共用接口,共享同一份数据来源,避免运营填写多份数据,节省运营成本,又因为移动端大量个性化推荐的需求,千人千面的展现方式,所以移动端Web首页采用了异步渲染的方案——页面进入只载入框架,再调用接口获取数据和模板,在浏览器端进行渲染。
    这样做也有诸多好处:客户端中访问可以使用离线包、更少的流量消耗、更细粒度的ABTest、对接其他数据源更方便等等。
    同时还实现了一套可以快速满足区块调整需求动态化布局方案,完美地支持了多次需求变更而无需前端代码发布,详细可见发表在天猫前端团队博客的这篇文章,让需求来得再猛烈些——快速响应需求的天猫H5首页新架构 · Issue #35 · tmallfe/tmallfe.github.io · GitHub

    3. 光影融合,Web和客户端的交汇

    相比起客户端业务迭代的成本、体积和发版劣势,Web的低成本、迭代快、跨平台的特性,使得绝大多数客户端的业务都会选择嵌入Web的方式。而客户端的黏性,使得这个Web页面相当大一部分的流量都来源于客户端。

    那么一个新的问题来了:『客户端中的Web页面如何利用客户端的能力?是不是可以将Web和客户端进行融合,获取更好的体验?』

    最初尝试了几个方案:

    • Hybrid API。
    • 跳转部分高频页面根据Url规则拦截到Native页(如商品详情页)。
    • 复用客户端建立的网络链路
    • 离线包等等

    都取得了一定的效果,但是还需要一套更加完整彻底的方案。所以在2015年7月React Native如火如荼的时候,天猫首页立刻开启了尝试的项目,相关内容后文详述。

    三、疾如风——天猫首页性能优化

    性能一直是首页所需要关注的要点,希望能给用户更好的体验。

    首先明确一点,有很多优化不是单纯的前端团队能够完成的,有CDN团队和服务端团队的支持,让前端同学感觉像生活在极乐天国。《高性能网站建设指南》这本书里提到的方法都比较基础了,在此略过。

    除此之外,我们还做了以下对于性能有着显著影响的事情:

    1. css/js文件combo。我们的CDN和前端Loader支持实时url的combo,可以实现按需加载。如果CDN没这个条件,也可以使用webpack等前端工具进行合并打包。
    2. 懒加载。优先渲染首屏内容,其余模块资源延后或者用户有交互的时候加载。
    3. Webp图片格式。对比这两张图,可以发现Webp格式的图小得多,WebP格式的图:img.alicdn.com/tps/TB1l;jpg格式的图:img.alicdn.com/tps/TB1l。感谢(图片存储系统的同学)。
    4. 裁剪合适尺寸的图片。如果某一张图片运营投放的尺寸过大,可以根据实际的尺寸,加上后缀截取,如『img.alicdn.com/tps/TB1l』。
    5. 拍扁所有请求。每个模块渲染所需要的请求不能是两个请求串行。
    6. 数据请求支持自由合并。(感谢服务端同学)
    7. 前端首屏只请求后端一个请求,由后端做对接其他系统的并行请求。
    8. 利用动态化布局保证模块之间的高复用
    9. 图片域名收敛。因为CDN支持SPDY协议,所以收敛域名可以有效节省DNS解析时间。
    10. 天猫前端组件规范整体升级,移动端去KISSY,变得更加轻量,减少流量消耗。
    11. 离线包。如果页面是纯异步渲染,可以考虑在客户端中设置离线包。只有当用户处于wifi且网络空闲时,客户端才会去下载离线包,二次访问效果拔群。

    再让我用一张图说明页面渲染各个阶段可以做的优化。

    除了加载性能外,还需要考虑交互性能,例如页面滚动时的帧率。
    除了用requestAnimationFrame来将模块渲染切片,避免UI线程阻塞。如果页面的hover和mouseenter效果过多,还可以在用户滚动时,设置全局的pointer-event:none,避免在滚动的时候触发大量hover效果,实测效果拔群。

    四、侵略如火——大胆的Native化尝试

    相比起Web而言,APP有着更好的体验;但相比APP,Web的开发效率和发版优势毋庸置疑,如何融合两者,一直是前端的重要探索方向之一。

    1. ReactNative的初登场

    2015年7月,天猫首页立刻进行了ReactNative的尝试。

    但在尝试过程中遇到了非常多的问题,满满的坑和血泪:

    • 一方面是ReactNative彼时并不完善,调试体验非常糟糕,莫名其妙红屏不知原因,还有升级0.8.0后会顺手升级iojs为3.x,和2.x的iojs并不兼容。
    • 另一方面是当时考虑并不完备。工程化落地一个技术需要考虑的因素非常多:多版本降级&兼容方案、开发调试工具、环境一致性、埋点、测试、监控、后续维护成本等等,需要考虑一系列的方案。

    因为没有拿到数据和结果,并且没有足够的人力维护多一个版本的页面时,RN版的首页选择了停止维护。
    但是ReactNative在天猫和双十一还有大范围的应用,详细可以看《天猫双11前端分享系列(三):浅谈 React Native与双11》天猫双11前端分享系列(三):浅谈 React Native与双11 · Issue #27 · tmallfe/tmallfe.github.io · GitHub

    2. WeeX的回归

    正如灰太狼被打飞时常说的一句话——『我还会回来的』,在手淘团队推出了一套新的Native化方案WeeX以后,天猫首页又死性不改蠢蠢欲动地进行了尝试。

    这一次考虑较为全面,上述RN落地时没考虑的方案都有一定的考虑和解决,例如自动构建Web版本以降低维护成本、多Weex版本的降级方案、Cache集群部署Detector自动识别请求是否支持WeeX并返回对应内容。实际体验优于Web,从业务数据上来看也取得了较好的结果。

    感兴趣的同学可以使用iOS手机淘宝客户端5.7.0以上版本扫码下述图片体验。
    img.alicdn.com/tps/TB1G

    Weex页面非常精简,gzip后首页JSBundle的大小大概是13K,是Web的十五分之一。

    WeeX即将开源,敬请期待。

    五、不动如山——天猫首页的稳定性与保障

    大概是心有灵犀吧,天猫首页和淘宝首页也采用了非常类似的容灾和监控机制,有小胡子哥的详述,我在此就一笔带过。

    1.在容灾方面:

    • 如果接口请求失败,会依次加载本地缓存、访问CDN的备份文件等。
    • 对于运营填写的数据内容质量,投放系统配置了详细的校验,如最大字数等等。

    2. 在监控方面:

    • 全量监控的埋点方案。能够全网统计到各个模块的运行状况和接口调用的健康情况。
    • 客户端层面的监控方案。能够拿到更多Web运行时无法自己拿到的信息,如当前页面某一链接404。
    • 业务监控的方案。能够保障页面的业务内容一定是符合预期的。因为首页的数据来源相当多,一个页面对应几十个运营,对于数据内容的质量是一定要保障的。

    六、小结

    接下来天猫首页还会做的事情大概有:

    • 赋能运营。接入个性化合图系统,免去了运营找设计师做图的成本等等;
    • 国际化。为了方便弱网地区用户的访问,对于性能有更高的要求,对于设计、素材也有更高的要求。

    提笔匆忙,感觉虎头蛇尾,还请见谅。
    如果有不明白的地方欢迎指出,欢迎和我一起来完善这篇文章哈哈。

    最后,天猫前端团队正在诚意招资深前端开发工程师,简历请发到yueyuan.cx@alibaba-inc.com

  6. 更好的阅读体验,请戳这里:团队博客(聊一聊淘宝首页和它背后的一套 )或者我的个人网站(聊一聊淘宝首页和它背后的一套)。

    聊一聊淘宝首页和它背后的一套

    从 14 年双十二结束开始接手淘宝首页,到如今差不多 1 年半时间,不久前完成了首页相关工作的交接。期间经历了两次改版和一次从 PHP 到 Node 的迁移,还是颇有感受,下面给大家分享下。

    文章好像有点长,列个大纲会比较好:

    一、相关背景介绍
    二、淘宝首页的整理变迁
      1. PHP 下的淘宝首页
      2. PHP 到 Node 的变迁
      3. Node,不一样的模式
    三、淘宝首页的性能优化
      1. 页面渲染逻辑
      2. 一起来看看淘宝首页的个性化
      3. 淘宝首页性能优化实践
    四、淘宝首页的稳定性保障
      1. 兜底容灾机制
      2. 监控预警机制
      3. 上线前的自动化检测
    五、淘宝首页的敏捷措施
      1. 健康检查
      2. 接口 Hub
      3. 快捷通道
    六、小结

    一、相关背景介绍

    淘宝首页是淘宝的门面,承载着几乎淘系所有业务的入口,流量很大,量级单位为亿。近几年无线端崛起,业务重点开始向无线终端偏移(目前不能叫偏移,基本以无线为主了),所以淘宝 PC 端首页的流量也有削减,不过即便如此,它的日均 PV 依然相当高。

    淘宝首页一向是内部平台和技术的试验田,它一直在变化着。最新的框架和系统都会找淘宝首页试点,可以试想下,如果某一项需要推动的升级或者优化措施在淘宝首页已经上线,并且拿到了良好的数据和稳定性,其他业务还有什么理由不去尝试和更迭呢?同时,去年一年身在淘宝前端的技术架构组,自然而然也会主动去 push 一些实验性的内容到业务上。

    淘系的站点页面包括首页、其他频道页和活动页等,这些页面并不都由淘宝前端一行一行的代码码出来,业务如此之多,这种玩法即便人数 double 也忙不过来。事实上,大多数页面都是依托内部的搭建平台——运营或者前端通过模块搭建的方式——构建的,而前端 focus 的重点在于搭建平台的建设自身以及模块的通用性和复用率的保障,当然,还有一些工程化的东西。

    使用搭建平台搭建的页面,前端只需要考虑组成页面的原子模块的开发,整体的渲染由搭建平台提供的统一脚本全权负责。而在淘宝首页上,考虑到页面模块数量巨多,加上还有少量跨部门、跨团队的沟通,渲染模型略微不同。

    二、淘宝首页的整体变迁

    背景中提到,淘宝首页依托于内部搭建平台,它的变迁自然也是跟着搭建系统的变化而变化的。

    1. PHP 下的淘宝首页

    接手淘宝首页不久,便遇到了一年一度的改版,那时它还运行在 PHP 环境中。这里需要说明的是,淘宝首页的所有代码完全由前端掌控,前端不会直接跟数据库打交道,其数据来源分为两部分。

    数据来源

    一是 运营填写的数据。 采用前端挖坑的形式,预留坑位让运营获取填写数据,如(伪代码):

    < ?php $info = Person('name:String:姓名,age:Number:年龄', '个人信息坑位填写');?>
    
    < div>
    < ?php $info.forEach(index) { ?>
      Name: < ?= info[index].name ?>, Age: < ?= info[index].age ?>
    < ?php } ?>
    div>
    

    上面的代码会产生一份 PHP 的模板和 info 字段对应的表单坑位,这个过程简称「挖坑」。

    运营填写这些坑位就会产生这份 PHP 模板对应的数据,最后渲染出来就是一个完整的 HTML 片段(实时性渲染)。

    .
    ├── data.json   # 运营数据的来源
    └── index.php   # 装载运营数据的 PHP 模板
    

    旧版搭建系统中就是通过这种方式构造一个子模块。我描述得十分简单,但作为一个平台它需要考虑的东西还有很多很多的,比如数据顺序的控制、定时发布、回滚机制、过滤机制、筛选机制、数据的同步、数据的更新、版本控制、权限控制、其他系统的引用等等。

    二是 后端或者个性化平台提供的数据。 不同的业务有不同的诉求。一些业务有自己的后端,他们要求使用自己业务产出的数据;有的业务希望用户看到的内容不一样,千人千面,期望接入算法;一些业务跟卖家直接打交道,期望使用招商数据;而有些业务期望采用运营从数据池筛选出来的数据…总之,淘宝首页需要对接形形色色的系统,接口繁多。后面会提到对动态数据源的整合。

    并且这些系统对应的域名是不一样的,JSONP 格式自然也就成了首选。但一些特殊的系统,比如广告,它的渲染并不是一个简单的 JSONP 请求,可能它还要干预整个广告的渲染流程,比如加载他们的 JS,把渲染的控制权交过去。

    页面的架构

    上面介绍了数据的来源和子模块的结构,那么整个页面又是如何构成的呢?模块的搭建分为两种,一种是可视化搭建,运营或者前端可以将开发好的模块(或者模块库中选取的模块)拖拽到容器内,形成一个页面,

    当然,上图也只是一个模型,作为一个系统需要考虑的问题还有很多很多,如页面的布局、多终端适配、模块的临时隐藏、位置调整、皮肤选择、模块的复制等等。

    也可以通过如下源码搭建的方式(伪代码):

    < body>
      < ?= loadModule(Mod1ID) ?>
      < ?= loadModule(Mod2ID) ?>
      < ?= loadModule(Mod3ID, 'lazyload') ?>
      < ?= loadModule(Mod4ID, 'lazyload') ?>
      < ?= loadModule(Mod5ID, 'lazyload') ?>
    body>
    

    通过模块 id 将模块引入,并且添加一些类似 lazyload 的标记,方便控制渲染节奏和数据入口。源码搭建和模块搭建的区别在于,前者更易于控制模块的结构以及模块的渲染顺序。

    动态数据源

    首页面对一大堆接口和平台,对接几十个业务方,接口是个很大的问题,由于后台系统的差异,基本没有办法统一数据源的格式,一旦运营哪天心血来潮要换一个他自己觉得用的更爽的或者数据更好的系统,前后端估计又得沟通和对接几次。所以出现了下面这张图:

    平台具备数据源接入的能力,也就是说我们挖的坑不仅仅可以让运营填数据,还可以从各种数据源中直接导入数据,当然,这里需要进行一次数据字段的映射转换。后端提供的接口是这样的:

    {
      "data": [{
        "item_name": "name",
        "item_url": "http://xxx",
        "item_pic": "http://xxx"
      }]
    }
    

    前端约定的接口形式是:

    {
      "info": [{
        "name": "name",
        "url": "http://xxx"
      }]
    }
    

    那么系统必须提供这种映射的绑定策略:

    info/name -> data/item_name
    info/url -> data/item_url
    

    绑定之后,数据既可以同步输出,也可以异步输出,这些都是平台提供的能力。这个方案基本上解决了后端系统/接口变化的问题,并且减少了前后端之间的沟通成本。

    不过这里需要注意的是,虽然页面上的接口都通过平台统一梳理了一次,这也意味着,页面所有的请求会先流经平台,然后分发到各个后端,平台的抗压能力要求很高。

    2. PHP 到 Node 的变迁

    淘宝首页日均请求的这个量级,不可能是十几二十台台服务器抗得住的,支撑它必须有一个服务集群。

    每一个 CDN 节点上都具备 PHP 渲染的能力,当页面发布时,我们把该页面所有的模块和数据同步到全部 CDN 节点上,基本模式大概就是如此了。看起来还挺不错,但是经过一段时间的运维,很多安全、性能问题都慢慢浮现出来了:

    性能问题。 每个 PHP 页面包含多个子模块,而子模块也有可能引用了其他的子模块,PHP 的include 操作是存在消耗的,每一次引用都是一次磁盘 IO,一个渲染节点上跑了成千上万个类似淘宝首页的 PHP 页面,并发一高其效率可想而知。

    推送机制问题。 文件同步(图中的 sync 动作)是一种比较恶心的机制,首先,时间上没法控制,一个文件同步到所有的节点,快则几秒钟,慢的话耗时会超过一两分钟;并且同步过程还有可能失败,健康检测的成本也是相当高的。发布比较紧凑时,需要同步的文件也很多,很容易造成队列堆积,加剧同步差的体验。

    实时性强需求问题。 文件在推送之前,还可能经过一些前置系统,发布链路越长,线上生效时间越慢,慢的时候大约五分钟才生效,这样的延时对于实时性要求很高(如秒杀)的需求来说是完全不能接受的。

    当然,还有很多其他问题,如运维成本增高、安全风险增高、PHP 资深人才储备不足等等。所以 PHP 渲染容器的命运,就是,被干掉。

    上图改变了下玩法,服务集群为 Cache CDN,它只有静态文件处理能力,没有 PHP/Node 的渲染能力,所以处理效率高,性能也好,抗压能力相当强,并且扛不住的时候还可以花钱买服务,拓展 Cache 集群。

    用户访问时,Nginx 转到 Cache CDN,如果命中缓存则直接返回,没有命中便回源到源站服务器。源站服务器是具备模块渲染能力的 Node 服务,它可以做很多事情:

    • 控制 Cache 响应头,通过 max-age 和 s-maxage 控制页面在客户端的缓存时间以及在 Cache 上的缓存时间,这个缓存时间可以根据需求随时做调整,比如大促的时候调长一些
    • 控制内外网环境,和 AB 测试状态
    • 融合前端相关的工具链,比如检测、压缩、过滤等等

    它的优势有很多,这里不一一列举了。这个模式中还添加了一层容灾,源站服务器每隔一段时间将数据推送到于 Cache 同机房的备份服务器,一点源站挂了,还能够自动容灾到备份数据上。

    模式的变化不仅在运维上有了突破,CDN 被攻击时的安全风险也低了很多,同时也省却了 sync 所需的各种检测机制,每年节约成本也是百万以上,优势还是相当明显。

    3. Node,不一样的模式

    上面 PHP 模块中,我们只说了 HTML 和数据部分,用心的读者应该已经发现,CSS 和 JS 这些静态资源都没提到,那页面是如何渲染的呢?

    旧版 PHP 页面中,我们是直接引入了一个 CSS 和一个 JS,淘宝这边采用的是 git 版本迭代发布,这些静态资源都是直接放在一个 git 仓库中。也就是这样:

    < head>
      < link rel="stylesheet" href="//cdn/@VERSION@/index.css">
      < script src="//cdn/@VERSION@/index.js">script>
    head>
    < body>
      < ?= loadModule(Mod1ID) ?>
      < ?= loadModule(Mod2ID) ?>
      < ?= loadModule(Mod3ID, 'lazyload') ?>
      < ?= loadModule(Mod4ID, 'lazyload') ?>
      < ?= loadModule(Mod5ID, 'lazyload') ?>
    body>
    

    每次发布完 git 文件,再修改 PHP 的版本号,然后发布 PHP 代码。当然,也做了相关的优化,比如发布 git 时自动更新版本号等。

    而新版搭建平台的页面渲染模式与 PHP 的模式不太一样。

    一个模块的 CSS/JS 和模板放在一起,CSS/JS 与页面其他模块的静态资源是相互独立的,目的就是希望单个模块也能够完整的跑起来,更加利于模块的复用。

    而模块的挖坑,也从模板中独立了出来,采用 JSON Schema 的形式定义数据格式,

    .
    ├── index.css    # 模块样式
    ├── index.js     # 模块渲染脚本
    ├── schema.json  # schema 配置
    └── index.xtpl   # 模块的模板
    

    搭建平台通过这个 JSON Schema 解析成 图一 的坑位。那么一个模块的渲染就编程了index.xtpl 和挖坑数据之间的拼装了。

    模块之间相互独立隔离,所以会存在一定程度的冗余,不过模块接偶带来的收益要比这点冗余要多得多。事实上,我们是通过一个仓库去管理单个模块的。页面的渲染就比较简单了,源站 Node 容器会将所有的 index.xtpl 合并成一个 page.xtpl,为减少页面请求,css 和 js 也会 combo 成一个文件,如上图所示的 cdn/?

    任何模块的更新,页面都会有感知,下次进入系统时,就会提示是否需要升级模块和页面。这里内容比较多,我不细说,感兴趣的可以找我 私聊。

    三、淘宝首页的性能优化

    首页模块众多,如果一口气吐出来,DOM 数量必然超过 4k 个,其结果就是首屏时间极长。按照 TMS 的开发规范,每个 TMS 模块都包含一个 index.js 和 index.css,最后展示出来两个 combo 的 js 和 css。首页加载的时候也不会一口气执行所有 index.js,否则刚开始页面阻塞会十分严重。

    页面的渲染逻辑

    首页框架的加载逻辑,大致上图所示:

    • 遍历所有 TMS 模块(包含一个 J_Module 的钩子)
    • 部分 TMS 模块无 JS 内容,但是加载了一个 index.js,为模块添加 tb-pass 的 class,用于跳过该模块 JS 的执行
    • 将页面分为两块,首屏为一块,非首屏整体为第二块,先将首屏模块加入到懒加载监控
    • 待首屏模块加载完成,或者用户处理了页面交互时(滚动、鼠标移动等),将非首屏模块加入到懒加载监控
    • 处理一些特殊模块,它们会在进入视窗之前几百像素就开始加载
    • 监控滚动,按照以上逻辑,渲染模块

    部分模块即便是被执行了,也不一定渲染出来,因为它的优先级不高,在模块内部加了事件监听,比如等到 mouseover/onload 事件触发的时候再渲染这些内容。

    之前写过性能优化相关的文章,复制就没必要了,直接贴地址:

    • 《一起来看看淘宝首页的个性化》
    • 《淘宝首页性能优化实践》

    代码的性能优化是一个精细活,如果你要在一个庞大的未经优化的页面上做性能优化,可能会面临一次重构代码。

    上面的文章提到的是页面内部的细节优化,但是在开发流程中做的规范化、标准化,以及线上访问通路中的各个环节优化还没有提及。这一块内容可能有点跑题,就不多说了。

    四、淘宝首页的稳定性保障

    在大流量下,任何小问题都会被放大成大问题,所以开发环节遇到的任何偶发性问题都需要引起重视。不过很多偶发性问题在我们的测试环境中是找不到的,比如与地域相关的问题(如上海的某个 CDN 节点挂了),用户属性问题(如 nickname 最后一个为字母 s 的用户页面天窗),浏览器插件问题,运营商广告注入问题等等。

    难以在上线之前把所有问题考虑周全,但是有两点是必须做好的:兜底容灾 + 监控预警。

    1. 兜底容灾机制

    兜底容灾有两个层面的考虑:

    • 异步接口请求错误,包括接口数据格式错误,接口请求超时等
    • 同步渲染,源站页面渲染出错

    异步接口请求,主要涉及到的是后台系统,对接系统较多,各个系统的稳定性和抗压能力各不相同,这方面的保障有多种方案,下面是最常见的:

    每次数据请求都缓存到本地,并且为每个接口都提供一个硬兜底。还有一种方案是「重试」,请求一次不成功那就请求第二次。这方面的讨论具体可以看看之前写的这篇文章:《大流量的下兜底容灾方案》。

    对于同步渲染,它只需要页面模板和同步数据,两者中任一种存在错误,源站都会报错,此时回源返回的内容就是一个 error 页面,状态码为 5xx。这个错误不一定是开发者造成的,有可能是系统链路出现同步异常或者断路问题。针对这种问题,我给淘宝首页做了一个镜像页:

    一旦源站任何异常,Nginx 都会转到与 Cache CDN 同机房的首页镜像上去,这个镜像内容就是淘宝首页的 HTML 备份源码。

    2. 监控预警机制

    可以先看看之前写的这篇文章:《前端代码异常日志收集与监控》,介绍了一些监控方法。

    监控也有两个层面:

    • 模块级别的监控,接口请求布点、模块天窗检测等
    • 页面的监控,在页面上添加特殊标记,定时回归所有 CDN 节点,查看特殊标记是否存在

    模块层面的监控,内容还是相当多的,监控的点越多越详细,到最后定位问题的效率就会越高,比如在一个稍微复杂的模块上,我会埋下这些监控:

    • 接口请求格式错误、请求失败、请求超时,至少三个埋点
    • 硬兜底数据请求失败埋点
    • 模块 5s 内没有渲染完成统计埋点
    • 模块内链接和图片黑白名单匹配埋点

    其中部分监控还会自动处理明确的错误,比如 https 页面下出现了 http 的图片,会立即自动处理掉这些问题。

    3. 上线前的自动化检测

    这属于淘宝整个工程化环境的一部分,前端自动化测试。一般会在上线之前处理这些问题:

    • 检测 HTML 是否符合规范
    • 检测 https 升级情况
    • 检测链接合法性
    • 检测静态资源合法性
    • 检测 JavaScript 报错
    • 检测页面加载时是否有弹出框
    • 检测页面是否调用 console.*
    • 页面 JS 内存记录

    当然,也可以自己添加测试用例,比如检测接口数据格式、模块天窗问题等。自动化检测也可以设定定时回归,还是比较有保障的。

    五、淘宝首页的敏捷措施

    1. 健康检查

    页面模块众多,为了能够追踪页面上每一个小点的变化,我在请求、渲染的每一个环节都做了详细的统计,如下图所示:

    一旦接口请求失败,或者接口走了容灾逻辑,或者模块渲染超过 5s,控制台都会有黄色警报,当然此时,也已经向服务器发送了警报统计。

    2. 接口 Hub

    接口 Hub 是对数据请求的管理工具,如下图所示:

    页面很多模块的渲染都需要一个以上的数据源,一旦运营反馈页面渲染数据异常,可以直接通过 Hub 找到数据,加速 Bug 定位效率。同时 Hub 也可以用来切换环境,将一个接口的请求切换到日常或者预发环境的接口之中,它是调试的利器。

    3. 快捷通道

    我在页面脚本执行前后都放了一个快捷操作通道,一旦遇到紧急线上问题,比如样式错乱溢出、接口报错导致天窗等,可以通过快捷通道直接修改页面的 CSS 和 JS,两分钟内上线。

    不过这类通道只适合紧急问题的修复,毕竟随意插入 JS 代码是存在很大风险的。

    六、小结

    写的好像有点虎头蛇尾(码字和画图都太累),还有很多方面没有延伸拓展开。希望以上可以让你对淘宝首页有一个基本的认识。

    可以戳这里(Taobao FED | 淘宝前端团队)更多的了解淘宝前端团队。

  7. 说几点收益非常巨大而且常用的吧:

    cdn(html & assets)

    cache(client&server)

    lazyload & lazyrender

  8. 首先介绍一下背景,我是从事app服务端开发,曾负责过app服务端架构设计工作,工作早期曾接触过linux桌面GUI程序的开发,主要是Qt以及一点点gtk。
    用户体验最重要的莫过于系统流畅度了,这并不是说接口速度一定要非常快,渲染速度要非常快,而是你总能响应用户的操作,比如手势的滑动,鼠标的拖拽、点击。要达到这一目标,不是服务端尽可能提高接口速度就能满足的,它一定是客户端和服务端双方共同努力的结果。其实很简单的动作就可以大幅改善响应速度。

    首先需要避免的是一个页面一个接口。
    很多做移动端前端开发的为了省事,喜欢一个页面一个接口,这就会给服务端带来一个大问题。假如服务端为了整合这个页面所有元素,调用了10个接口,这个接口的速度基本上属于线性叠加10次接口的调用时间,同时接口发生异常的概率也被放大了10倍,即便动用了非阻塞式并发调用优化,时间也取决于最慢的那次调用,同时大规模的并发异步回调也会让服务端的代码变得难以维护。
    好处很明显页面元素分开请求,一次应答的数据量就少,某些元素就会非常迅速的展现出来而不会受到慢 的数据的拖累。(粒度大小需要具体情况具体分析)
    可能带来的成本上升:页面需要管理多个请求,代码难维护。

    其次需要尽可能少的使用for循环渲染。或者说减少for循环里面的次数。很多前端为了追求轻量,几乎把所有的逻辑放到了后端,而仅仅作为一个数据展示的界面,所以在拿到后端吐出的数据的时候,需要做的仅仅只是去set控件,这样也是不利于优化的,假如接口吐出来的数据量很大,前端拿到数据之后一个类似

    for(data:dataList){
        xxx = new Item();
        xxx.setLabel(data.xxx);
        xxx.setText(data.xxx);
        xxx.setContent(data.xxx);
        ......
        xxx.renderTo(listView, SIG_APPEND_ELEMENT);
    }
    

    的代码去各种set控件,会带来什么问题?会导致主线程一直停留在这个巨大的for循环上,表现出来的现象就是假死,手势无法响应,如果是桌面程序,程序会僵掉。
    其实这些在早期的应用中算是基本的优化手段,然而它们在这个浮躁的移动互联网时代并没有得到沉淀。导致移动端上面拥有良好用户体验和迅速回馈的应用并不多。
    你只需做到以上两点,你的app程序的用户体验会改善90%响应会直线上升,然后就可以去掉为了改善用户响应而创造的伪过程动画,从而精简容量以及进一步提升响应速度。

    怎么样做到以上两点?
    其实也很简单,我认为websocket技术能同时解决好上面两个问题。
    试想,一个应用只需要通过一个socket长连接与服务端交换数据,而不是http那种问答式,这样客户端很多请求就没有必要追求一次性返回,假如客户端某个页面进来有十个控件需要渲染数据,那么,客户端可以通过这个长连接一口气发送10个请求,说不定客户端在发送第8个请求的时候,第一个回应就过来了,再通过回调进行渲染。服务端针对这10个请求的处理速度肯定是不一样的,所以结果的吐出也是有先后的,处理快的先吐出去,处理慢的后吐,这是很重要的,也避免了客户端“同时”渲染一整块页面而导致卡顿。

    客户端还要怎么优化呢?客户端主要的还是需要避免一次性渲染太多,如果数据量很大,那么只渲染一部分,停一停做做别的,再渲染一部分,停一停做做别的,反正就是需要“停一停”,这时可以释放出主线程,响应一下手势或者动画。这种思想就类似于服务端的”并发““协程”,假如我通过epoll拿到一个fd,接下来就是完全读写完这个fd,试想这个fd是一个用户上传10GB文件的句柄,你这么玩,其余fd不就迟迟得不到响应吗?所以通常我们只会读一个固定长度到buff中,管它有没有读完,赶紧去处理别的fd,然后再次等待epoll的调度。从而提升“响应速度“,这种思想其实前端更需要,然而有后端意识的前端程序员太少,这就是市面上很少见优秀的应用的原因,要知道,在孱弱的单片机上跑qt,为了流畅度真是无所不用其极。到了2016年的今天,手机性能如此爆炸的今天,却充斥着大量卡的一逼,烂的一逼的应用。

    我们来探讨一下,客户端怎么做到”停一停“呢?答案是建模,拿到接口吐出的数据之后通过多线程(0)去setModel ,一来灵活的调度策略可以从模型中择机通知主线程来渲染一定量的控件,二来提高了模型的复用多个view复用模型就不需要频繁发送请求,具体的技术方案很多,就不介绍了,也包括可以使用select或者poll做事件驱动。更加精细化的优化还有预渲染一定数目的空条目占坑,然后setText之类的基本消耗就很小了。加速度判断更多加载以及像chrome那样动用数据分析来预测用户行为从而预测型加载某某数据等等,总之看你脑洞大不大了,这里面的门门道道没有定势,完全取决于你的想象力和创新能力。当然web端没有多线程以及select,epoll这种高级设施可以利用,web的话,chrome为例,最小时间片貌似是4毫秒,可以用4毫秒为停顿周期,一次渲染100个tag,做为框架。

    可以看到一个真正对用户体验有追求的前端是无法轻量的,至少从代码层面而言。

    受阿里前端架构的影响,包括评论中也有人提到的,越来越多的玩家使用nodejs来倒腾一遍数据做一下页面渲染,然后再吐给用户浏览器,这对于绝大部分公司来说是没有必要的。

    首先它反应出了阿里当初的前端架构理论不足,而在那种情况下硬生生的把淘宝天猫给搭建起来了本身也不容易,而后来当restful理论开始大行其道的时候,阿里则在背离restful理论的道路上越走越远了,但是不管是restful也好,还是阿里的hsf+webx+velocity模板+nodejs异步拼接模板也好,都是在各自的道路上努力实现前后端分离这一主旨。

    restful json api 本是目前理论上最好的前后分离架构,服务端只需要暴露http json接口,一套接口可以供各种前端使用(包括客户端,客户机服务端,广义前端),html js css则完全存放在文件服务器上,由用户浏览器来实现异步拼接数据渲染页面,完全不会去使用模板引擎。这种架构适合构建html5单页面应用(当然传统PC页面也同样适用),唯一的问题是对百度爬虫不友好,不知道百度何时能上sandbox。

    阿里则走的是另一条路——rpc和模板,nodejs来异步加载就很好(有点类似于异步读多个文件),由于技术选型的落后,但又在本身落后的选型基础上进化了大量配套设施,有点蒸汽朋克的感觉,以至于在不伤筋动骨的基础上为了实现前后端分离,才有了nodejs这么一个二道贩子存在。这是一个阿里为了偿还技术债而做出的努力,阿里才有培育它的土壤,却未料到引起跟风。非常不建议别的公司生搬硬套,到时候只会东施效颦让人笑话,完全可以甩开膀子用更加纯粹和先进的技术大步向前。(1)所以啊,不能见风就是雨,你们看到的这些个东西啊,本身也要判断。。。

    对于没有历史包袱的创业公司来说,后端只需提供restful风格的json接口。html,css,js可以直接放在文件服务器(包括CDN节点)加速访问,直接让用户浏览器(2)异步访问restful接口渲染数据,这样做可以让浏览器以最快的速度渲染出页面的骨架,然后内部控件也可以精细化的做分块请求。我个人更加推荐websocket来搞定整个应用,对每个请求业务编号bizcode,然后一口气闷给服务端,服务端回吐应答数据之后页面在on_message回调里面形成一个大的switch(bizcode) case转到对应的控件渲染上面,这样的体验才是最好的,符合当今应用设计标准,反应堆式编程。
    这么做比用nodejs中间转一道手清爽多了。

    与时俱进吧!
    (0):这种方案编码的难度其实挺大的(不信你们可以挑战挑战),难点不在于多线程setModel,而在于“择机通知”主线程来读取“一定量的数据”渲染控件(主线程很忙的而且主线程可不能被阻塞包括等锁,锁通知肯定是不行的,你好好想想如何实现对主线程的通知)。
    (1):能不能用nodejs?当然可以,但不要学阿里只用nodejs来渲染页面,要用nodejs就直接上nodejs好了,完全来取代web后端的增删改查。
    (2):chrome浏览器也是基于V8,nodejs也是基于V8,为什么不相信浏览器也能干好异步请求的事情呢?

发表回复