双十一狂欢的背后和NODE.JS不得不说的故事

覆盖业务

经过几年时间的改造和推进,到 17年双十一的时候,已经有大量的业务都有了 node 的身影,基本上天猫大部分的 web 页面都是通过 node 渲染出来:

  • 天猫首页、大部分天猫频道页、双十一会场以及所有天猫的活动页面都全部基于 node 应用提供服务。
  • 商品详情、店铺和搜索页等主流程链路上,以及天猫超市和天猫会员等业务线上的页面渲染。
  • 天猫页面搭建平台 web 层基于 node 进行开发,双十一期间在此平台上搭建了超过 1000+ 个双十一相关活动页面。

工作职责

在上述覆盖了 node 的业务中,node 在其中扮演了多种角色:

完整的 web 应用

天猫页面搭建平台即是一个由 node 负责整个 web 端包括业务逻辑和模板渲染等工作的应用。基于支付宝的 node web 框架 chair,通过 hsf 调用和淘宝共建的页面数据存储的接口,用 node 完成业务逻辑处理、页面渲染和前端接口。

轻量级的模板渲染容器

通过 node 整合前端的天猫组件规范 MUI,开发了一套专注于模板渲染的 node 容器(wormhole),通过这个 node 容器,前端可以专注于展现层的开发,统一前端的本地和线上的代码运行环境,也让后端摆脱了繁琐的套模板工作,专注于提供数据接口。同时这套容器基于天猫的模块化规范,横向打通了各个业务和应用之间的模块共享。

基于这个模板容器,完成了商品详情、店铺、搜索页以及超市等业务线上的前后端分离工作,大大提升了前端的开发效率,并有效降低了前后端沟通成本。

页面渲染服务

同样基于天猫前端的组件规范 MUI 和模板渲染的 node 容器,完成了一套模块化搭建页面的系统,同时开发并运维了一个用来渲染基于模块搭建的页面的服务,同时这个服务和阿里的 cache CDN 打通,在保证满足业务需求的前提下,降低消耗的计算资源。

基于这个服务,在双十一中提供了 900+ 活动页面的渲染,以及天猫首页和各个频道页的渲染工作,天猫的所有营销引流页面基本都由这个服务提供页面。

进入正题

上面讲了 node 做了什么,以及覆盖了那些业务,现在来看看,到底怎样用 node 解决实际的业务需求的。

拿这次双十一的会场页举例:

  1. 用户在不同的终端环境下访问 1111.tmall.com 这个网址,请求会直接来到 CDN 上。
  2. CDN 对用户的终端环境进行判断,并在内存中找到对应终端的缓存文件返回,若未命中缓存,则继续往下执行。
  3. CDN 将请求转发到 node 渲染服务,根据终端类型选择不同的页面响应(pc 页面,h5 页面, react-native 页面)。CDN 响应用户请求,并缓存页面。

在上述流程中,我们看到同一个 url 对应到后端其实是完全不同的页面输出内容,为了达到这个目的:

  1. 开发了一个 tengine-detector 组件,通过请求的 user-agent 以及约定的一些 cookie 信息,判断用户的终端类型。并部署到 CDN 上,让 CDN 拥有了终端判断的能力。
  2. 用户请求到 CDN 上之后,CDN 会根据用户的终端类型分类,设置一个请求头,例如: detector: pc 表明这个请求的终端设备是 PC 上的浏览器。
  3. 渲染服务获取到这个头之后,根据 url 和设备类型选择不同的页面返回。返回时设置 varydetector,保证 CDN 根据不同的设备类型缓存不同页面。

上面提到会根据终端类型对于同一个 url 返回不同的页面,而这些页面其实都是通过一个基于 node 开发的天猫页面搭建平台用模块搭建的。在这个平台上,超过 95% 的模块都拥有 pc 和无线两个版本,本次双十一所有用到的模块都有 react native 的版本。运营只需搭建 PC 上的页面,就会自动生成无线以及 react native 的页面。基于这套方案,通过 70+ 高质量的模块,让运营完成了超过 900+ 活动页面的搭建。

再深入一点,如何来完成这些页面或者是模块的呢?首先,让前端开发做什么?

  • 编写模板
  • 拿到数据(并处理),和模板进行结合
  • 拿到请求上下文,时间、环境等系统变量来确定不同的展现
  • 管理前端资源和依赖

在 xtemplate 模板引擎的基础上进行扩展,让前端通过编写 xtemplate 模板,在 context 中注入一些必需的页面上下文,扩展 xtemplate 的语法,支持引入前端资源。基于这套模板,可以在拿到数据后渲染得到完整的页面,基本满足了开发页面在功能上的所有需求。

但是页面中其实有非常多重复性的内容,完全可以把他们抽象成一个个的模块,让页面通过模块化的方式来基于模块搭建,在这个过程中需要解决几个问题。

  1. 模块版本和静态资源版本的管理:页面可能引用几十个模块,而这些模块依赖的静态资源有重复、有冲突,因此会通过一份统一的 seed 来进行依赖版本的管理,每一个模块在发布的时候都会打包好自身的依赖关系,而在将所有的模块组合成页面的时候,将所有模块的依赖表重新进行合并和去重,最终保证页面引用的模块和静态资源唯一。同时在模板中通过扩展引入了 FELoader(天猫的静态资源加载器),收集页面的所有静态资源,combo 后插入到页头(css)或者页尾(js)。
  2. 模块如何拿到相应的数据:对于模块而言,他并不需要知道被哪个页面引用了,所有的页面在引用模块的时候需要将模块所需的数据传递进去。而所有的模块开发者需要编写一份模块需要数据的 JSON Schema 描述,通过这份描述文件,搭建平台、投放系统以及其他使用这个模块的人都能够知道要为这个模块产生什么格式的数据。
  3. 配套的搭建平台和数据投放平台来让运营自由组合所有的模块生成页面,并为页面上的每一个模块进行数据投放。

解决完上述问题之后,将每一个页面都变成了以下几个部分:

  1. 一份页面的描述文件,声明了这个页面依赖的所有模块,以及渲染这些模块所需的数据的地址。
  2. 一系列相互独立的模块。
  3. 一份包含页面上所有模块需要的数据的数据文件。

最终,渲染服务会根据 URL 和请求的终端环境,找到对应的页面描述文件,请求相应的数据,合并所有的模板渲染成为 HTML 页面。

当完成了 web 页面的模块化搭建之后回头再看,是不是 react native(RN) 的页面也能够搭建呢?只需要所有的模块都有对应的 react native 版本,就可以像搭建 web 的 html 一样搭建渲染出 RN 需要的 js 了!所以本次双十一使用的所有模块都有 RN 版本,并有多个会场采用了 RN 进行搭建,取得了非常不错的效果,在接下来的双十二中,所有的会场都会支持 RN,而这一切对于搭建会场的运营来说都是完全透明的。

稳定性保障

对于 node 应用自身而言,首先要保证它有充足的测试,通过 mocha + istanbul ,尽可能让测试覆盖每一个功能点和边缘情况。

需要有完善的监控和报警。在阿里,已经有了内部的监控系统,对于 node 应用而言,只需要按照要求的格式打印的日志,或者通过自己编写日志采集脚本,就可以轻松的搞定监控和报警。

  • 错误日志监控:通过采集脚本采集上来并分类,并设置单机报警和阈值和集群报警的阈值,在异常出现时能够及时发现。
  • 系统状态监控:内存、CPU、load 等的监控,并设置报警阈值,当系统状态异常时能够及时发现。
  • 应用状态监控:QPS、响应时间以及所有的远程调用记录,时刻了解系统的负载和各个依赖节点的服务状态。

同时,对于 node 应用,可以使用 alinode ,他们可以提供更多 node 的日志和监控,并提供了在线的 profiler 和快照功能,方便排查线上异常和性能优化。

尽管可以对自身的代码做各种测试、各种监控,但是在一个复杂的系统中,各种上下游依赖非常复杂,网络情况也很复杂,这个时候为了保证稳定性,还有许多的工作要做。

没有单点

假设一个机房的光缆被挖断了,或者机房所在的城市大规模断电了,然后整个天猫的大部分页面都不能访问了,这明显不能接受,所以需要在多个城市的多个机房部署服务。如果存放模板文件或者数据文件的服务挂了怎么办?多个节点,主备读取,同时对所有的文件都加上磁盘文件容灾。对外提供服务的整条链路上的每一个依赖都不能够出现单点问题。

弱化依赖

在排除完单点问题之后,再来审视服务,是不是所有的依赖在挂掉后就无法正常服务了?是否对于每个依赖异常都有容灾的方案,弱化掉整条链路上的依赖。

预案自动化

对于每一个可能出现问题的环节,都需要有针对性的预案,如果这个预案需要人工去执行,就需要思考能否做到自动化。在 node 渲染服务中,可能有各个缓解出问题,链路上的所有预案都要能够自动切换:

  • CDN 回源到多个机房,当某个机房异常时能够通过健康检查自动剔除。
  • 当源站 load 过高时,服务自动切换到静态版本不做渲染。
  • 当模板或者数据的存储节点挂了,通过健康检查自动剔除。

总结

node 只是工具,在每一个具体的业务场景下都有最合适的使用方法,而随着业务的发展,node 能做的事情也在变化,期望它能在之后能在更多的场景下落地。


本文作者:掘金

原文发布于微信公众号 - 腾讯NEXT学位(NextDegree)

原文发表时间:2017-11-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张善友的专栏

为首次部署MongoDB做好准备:容量计划和监控

如果你已经完成了自己新的MongoDB应用程序的开发,并且现在正准备将它部署进产品中,那么你和你的运营团队需要讨论一些关键的问题: 最佳部署实践是什么? 为了...

48580
来自专栏lestat's blog

一个投票项目的总结

最近开发了一个只有3个页面的微信投票小项目 基本流程:一个微信号一天只能对一个参与者投一次票且一天总共可以对不同参与者投10次票 首页内容:展示所有投票参与者以...

31850
来自专栏架构师小秘圈

亿级浏览型网站静态化架构演变

作者徐昭,花名长恭,主要负责天猫详情系统的架构优化工作。毕业于浙江大学计算机专业,热爱Java Web技术,多关注服务端性能优化,热衷开源技术的研究和分享。 在...

39650
来自专栏开源优测

接口测试 | 26 基础及简要验证清单

## 什么是API API(应用程序编程接口)可以被看作是软件系统、服务、组件之间进行通信的桥梁。它约束了通信的基本规则。 简单的说,API接收用户的输入,并返...

374130
来自专栏小白课代表

Creo 4.0 安装教程。

23330
来自专栏跟着阿笨一起玩NET

C#轻量级高性能日志组件EasyLogger(六)

38520
来自专栏张戈的专栏

禁止百度转码和百度快照缓存的META声明

今天手机 site 中国博客联盟时,发现网被转码了,虽然这个网站没做移动站,但是我也不希望被百度转码,因为这相当于拦截了所有来自手机的流量。下面说一下禁止百度转...

39240
来自专栏木头编程 - moTzxx

微信公众平台开发[5] —— 微信扫码支付介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011415782/article/de...

12430
来自专栏码神联盟

架构 | 大型网站分布式高并发架构设计汇总

本文多数内容为小编精心总结,呕心沥血完成,切勿抄袭沿用。 参考文献《架构知识》、《深入理解java》 ---- 章节目录: 前言 前端架构 应用层架构 服务层架...

629100
来自专栏我是攻城师

重新认识同步与异步,阻塞和非阻塞的概念

在实际的开发中,我们经常会听到同步,异步,阻塞,非阻塞这些编程概念,每次遇到的时候都会蒙圈,然后就各种查网上似是而非的资料,结果越查越迷糊,大部分文章都千篇一律...

12920

扫码关注云+社区

领取腾讯云代金券