天猫浏览型应用的CDN静态化架构演变

jopen 11年前

        在天猫双 11 活动中,商品详情、店铺等浏览型系统,通常会承受超出日常数倍甚至数十倍的流量冲击。随着历年来双 11 流量的大幅增加,每年这些浏览型系统都要面临容量评估、硬件扩容、性能优化等各类技术挑战。因此,架构方面的重点在于,如何能够利用合理成本应对瞬间飙高 的峰值请求,并确保活动完整周期中系统容量的可伸缩性、用户响应时间的稳定性,以及外部依赖系统出现问题时的高可用性。此外,作为最主要的页面流量承载体 系,架构方面还需考虑防爬攻击、流控容灾等安全、稳定的需求,并综合衡量网络带宽、硬件成本、缓存效率等各方面要素,找准平衡点,从而达到以不变应万变的 理想效果。

        演进

        为此,自 2011 年起,以天猫商品详情系统为代表,天猫浏览型系统在架构上的主要工作之一就是通过静态化技术实现了动静态信息分离、利用缓存技术存放静态化内容、利用少量 动态数据异步加载填充。整个过程历经单机静态化、统一缓存接入,到 2013 年双 11 前彻底 CDN 化三个阶段(如图 1 所示),有效解决了缓存命中率、流量自然分布、系统扩容简化、用户端响应速度等关键问题。

天猫浏览型应用的CDN静态化架构演变

图 1  CDN 化的三个阶段

        目前,天猫浏览型系统最新使用的这套基于 CDN 的静态化架构,可以满足高可用持续伸缩的原始预期,并包含如下特性。

  • 动静分离:HTML 静态化和热点分离。
  • 分布式缓存体系:利用 CDN 节点分布式缓存。
  • 多级缓存机制:CDN 两级+应用一级。
  • 统一服务静态化集群。
  • 一致性维持:主动失效&自动失效缓存机制。
  • 动态内容填充:能支持多种时效性动态内容填充方式。
  • 监控预警机制:流量、失效、命中率等关键参数实时监控报警。

        本文将针对这一优化历程,就主要技术挑战、架构改造策略、最终优化成果做一个总览式的介绍,并重点对 CDN 化过程中整体架构的演进、缓存失效机制、动态内容填充等具体要点进行论述。

        第一阶段:系统静态化

        早期天猫浏览型系统大多采用简单架构,实现一层很薄的前台应用。以天猫商品详情系统为例,针对商品、用户等访问量较大的数据中心接口模式改造为 应用 Client 端缓存前置,同时普遍使用页面高速缓存(PageCache)来降低后端系统压力,使得整体可支持应用水平扩展不受限制。这一阶段系统面临的主要问题和挑 战包括以下几点。

  • 应用服务器瓶颈,页面渲染带来的 CPU 开销巨大。
  • 单纯基于 Java 端的缓存已基本覆盖,整体性能提升空间有限。
  • 水平扩容只能支持容量线性提升,难以满足大促井喷式流量增长,扩容成本高。

        从问题看,基于原有动态浏览型系统模式而优化的瓶颈很难规避,例如以下几点。

  • Java 应用服务器端必要开销,包括:涉及页面内容的字符串查找、替换、拼接等;元数据获取的网络开销;Servlet 本身的性能瓶颈。
  • Web 服务器端,包括:模块过滤,例如访问日志、Cookie 打点、繁简转换;大 HTML 页面本身的 GZIP 压缩等。
  • 突发流量的抵御,例如攻击、秒杀、大促,等等。
  • 已用优化手段达到了边界,包括:可使用缓存的地方已经使用;服务端 CPU 能力已优化完毕(模板解析、压缩)。

        总体来看,必须从架构着手彻底解决。架构优化的方向上,考虑以下 3 个方面。

  • 改变缓存方式,直接缓存 HTTP 响应结果。
  • 改变缓存位置,直接基于 Web 服务器,屏蔽业务逻辑。
  • 基本原则,缓存空间足够大、无单点、易于维护。

        为此,2012 年起正式启动了动态浏览型系统的改造项目,通过静态化手段解决上述问题。即基于业务把原动态系统中的内容做动静分离,对浏览者无关部分做缓存,动态内容做 CSI 填充。具体考虑从三方面重点着手展开:动静信息分离、静态化缓存方式,以及缓存失效机制。图 2 为一期静态化整体架构。

天猫浏览型应用的CDN静态化架构演变

图 2  一期静态化整体架构

        动静分离

        将原页面内容按业务进行区分,从浏览用户、信息发布者、时间、地域、私有(Cookie 等)信息等维度分析,抽取出页面中相对公共不依赖以上因素,且变化频度较低的内容作为基础,生成静态化内容。静态化后页面 URL 固定,不同 URL 表示不同内容,服务器返回的请求与 URL 相关,其他动态内容则通过异步接口调用,通过 CSI 方式填充。以商品详情系统为例,静态化后商品基本信息如标题、商品详情、销售属性组合等信息均直接进入缓存,其他如优惠、库存、物流、服务等动态信息则通 过异步调用方式填充至静态化后的页面框架内。

        缓存方式

        整体可划分为应用服务器、Web 服务器、CDN 节点、客户端浏览器 4 层缓存体系(如图 3 所示),分别承载不同使命。

天猫浏览型应用的CDN静态化架构演变

图 3  缓存整体划分

        缓存系统方面从开发成本、稳定性、I/O性能各方面综合考虑,选择了阿里内部广泛使用的分布式 key/value 系统 Tair,存取静态化后的页面。相对 Nginx 本地硬盘缓存方式来说,本地 Tair 读写性能更优,且服务器响应时间和负载波动影响小,使用及维护成本低。整套体系详解如下。

  • 应用层缓存:减小后端应用服务器压力,减少远程调用量。
  • Web 服务器缓存:减小后端应用服务器压力,抵挡瞬间峰值和/或针对少量定点内容的攻击。
  • CDN 缓存:合理地利用 CDN,内容缓存放置在离用户最近的地方,加快响应的速度。
  • 浏览器缓存:减少用户请求数量,降低系统压力,提升用户体验。

        缓存失效

        缓存失效主要包含“失效后台进行主动失效”和“缓存过期自动失效”两种机制。针对主动失效,主要技术难点包括以下 3 个方面。

  • 失效来源及监控范围:基于业务决定需要监听哪些数据源哪部分内容变更,通过变更消息接收执行缓存失效动作。
  • 每秒失效数据量级:单位时间内大量数据源(如商品、店铺装修)失效处理。
  • 要失效的缓存范围:支持批量(例如基于域名)和单个数据源缓存失效变更。

        以商品详情系统为例,失效来源主要为商品数据及店铺装修信息,后台用户修改导致对应内容发生变更时,通过消息机制通知失效后台。失效后台接收消息并保留待失效商品 ID,通过调用本地 Tair 接口失效缓存,大致流程如图 4 所示。

天猫浏览型应用的CDN静态化架构演变

图 4  缓存失效流程

        改造效果

        依然以天猫商品详情系统为例,采取静态化架构后,2012 年双 11 时,在性能方面,结合后期完成的店铺装修分离等优化工作,系统单机(实体机)在 80% 缓存命中率的情况下,安全 QPS(每秒查询率)相较 2011 年同期单机性能提升 7 倍多,系统资源则不到原来的 50%。与此同时,静态化还解决了单 URL 热点攻击问题,更重要的是,使得原动态架构下依赖的后端 Java 系统可以转变为弱依赖:一方面既通过静态化缓存层一定程度上保护了后端系统;另一方面在极限情况下,当后端系统不可用时,可以通过缓存维持一部分访问量。

        第二阶段:统一 Web 缓存

        第一阶段以商品详情为主的静态化架构改造取得了良好的效果,除天猫商品详情系统率先完成改造外,店铺等浏览型业务系统也很快参照类似方案完成了 架构调整。在过程中,逐渐确立了静态化技术规范,简化了接入步骤;同时,也发现在各自的系统中,尽管同样基于浏览型业务场景,但由于采用的缓存方案细节差 异,存在一些涉及静态化缓存体系相关的共性问题,包括以下几点。

  • 单机缓存静态页面,受部署模式影响,缓存层无法水平扩展。
  • 单机模式下,缓存受限于服务器能力及内存容量,命中率受制约。
  • CSI 模式填充动态内容,需要前端脚本配合,开发成本较高。

        因此,自然而然想到有必要统一 Web 缓存层接入,共享静态化集群以节省成本、提高稳定性和命中率。从运维角度看:

  • 统一接入层可以减少多个应用接入使用的成本,接入的应用只需维护自身 Java 系统,不用单独维护缓存;只要关心如何使用,统一的缓存框架也可更好地让更多流量型系统接入使用;
  • 统一接入层易于维护,并可统一加强全局监控、实现配置自动化,使集中维护升级更加便利;
  • 统一接入层可以共享内存,最大化利用内存,不同系统间的内存可以动态切换,有效应对攻击等类似突发情况。

        搭建统一接入层,需要针对各浏览型系统做局部改动。而整体需要重点解决的技术问题,从架构层次上看,主要涉及以下几大部分。

        缓存系统选择

        第一阶段各浏览型系统采用了单机缓存模式,基于成本、业务场景等各方面因素稍有不同。搭建统一接入层需要能够兼顾各浏览型系统的特殊要求,同时 还需能支持共同需要的 ESI 解析及 ESI 模式下 GZIP 压缩,完成静态页面局部动态内容服务端填充;性能方面,能够满足双 11/双 12 流量压力下的 QPS(每秒访问率)要求;支持失效协议以及长连接,可执行批量失效。综合以上分析,并考虑未来静态化内容最终 CDN 化部署方式,统一接入层 Cache 最终软件层面可支持以上所有功能,同时还包括快速失效和预热能力,支持 CSS 和 JavaScript 的脚本合并,长连接和批量失效,支持基于 HTTP 头的可编程配置等。

        统一失效机制

        与缓存软件变更对应,各接入统一缓存的浏览型系统需针对新的缓存体系及协议改造原有失效机制,使用公共协议标准来执行批量及单个对象的主动失 效。同时,建立了统一的失效中心和缓存校验层,所有接入应用的主动失效请求统一经由失效中心,通过 Purge 方式执行缓存失效。底层失效源方面,监控信息源数据变更。以商品为例,当商品编辑完毕,包括商品标题描述等更新后详情页面需要失效,基于实时监控和消息机 制进行主动失效(如图 5 所示)。

天猫浏览型应用的CDN静态化架构演变

图 5  基于事实监控和消息机制主动失效

        Web 服务器改造

        缓存层之前的 Web 服务层,需要能支持一致性 Hash 分组,并集成现有系统使用的 Session 框架,可支持基于域名虚拟主机的动态配置。为此,核心系统部门的同事自行开发了淘宝定制版本的 Nginx 服务器(Tengine),作为统一接入层之上的 Web 服务器层部署。

        网络流量支持

        统一接入缓存层后,由于集中了各系统缓存信息且访问集中,所以网络部署层次方面,可使用万兆网卡配置解决硬件瓶颈;同时评估集群需支撑的网络出口流量,确保机房内部及外部出口无瓶颈;在缓存不命中的情况下,需能支撑请求回源服务器端形成的内部流量。

        整体部署方案

        图 6 是整体部署方案,从中可以看出:

  • 统一接入层部署,包括前端 Nginx 服务器+缓存系统+后端 Java 应用部署结构;
  • Web 服务器层做一致性 Hash 分组;
  • 统一缓存层支持 ESI 或 CSI 方式获取动态内容;
  • 统一失效中心机制失效缓存。

天猫浏览型应用的CDN静态化架构演变

图 6  整体部署方案

        改造效果

        统一接入层于 2013 年上半年改造完成并开始了商品详情等浏览型系统的接入工作,完成后,在原有单机缓存模式之上又增加了一层集中式缓存,解决了缓存层的水平扩展问题。万兆网 卡的使用有效解决了缓存层的网络瓶颈。由于统一接入层与应用无关,因此可以多应用共用,使监控和维护成本大大降低,并提高了质量和效率。当然,这一改造也 造成应用对缓存层的强依赖链路,同时这一层缓存也存在单点问题。从静态化单机缓存模式到统一接入层,路只走了一半,一切改造的终极目标,是利用 CDN 分布式、地域性特性及强大的流量容量体系,实现浏览型应用的 CDN 静态化。

        第三阶段:CDN 静态化

        统一接入层解决了单机缓存内存使用率低的问题,摆脱了单机缓存受内存大小制约,在面对商品数量增加和商品热点分散的场景下,只能垂直扩展那些无 法水平扩展的问题,这提升了缓存系统的可维护性和扩展性。在完成系统从单机静态化缓存到统一接入层的架构改造之后,已经具备了将静态页面放置到 CDN 上的条件。CDN 提供了更强的服务能力,放置在离用户最近的节点上,是缓存系统单元化最理想的架构。同时,也为双 11 峰值流量和防攻击提供了更为可靠稳定的保障。

        CDN 化涉及 3 个具体技术难点。

  • CDN 分布式节点失效问题。方案:采用主动失效的方式,商品变更后主动发送请求给缓存校验层,由其通知失效中心,接收并分发处理节点失效任务,以确保秒级失效。
  • 命中率问题。方案:优化节点部署条件,CDN 节点数量可控,避免失效请求量过大,靠近流量集中区域,且节点到主站网络稳定;控制节点数量,访问流量集中分布在这批节点;节点内部采用类似统一缓存层的一致性 Hash 规则,以达到类似命中率。
  • 局部区域动态内容定时切换。方案:价格、库存等动态信息走动态系统接口,通过异步方式获取;展现端定时切换活动 Banner 等内容,走 ESI 回源,并同样缓存回源的静态资源。

        整体架构

        基于以上思路,总体架构已经较为清晰,方案上从缓存体系、失效模式、动态内容填充几方面入手执行改造,整体架构如图 7 所示。

天猫浏览型应用的CDN静态化架构演变

图 7  静态化整体架构

        缓存体系

        统一接入层和 CDN 节点上都是用 Web 服务器 +Cache 方式。静态化应用对应的域名会被解析到 CDN 和统一接入层的虚拟 IP 上,CDN 拿到请求后,先读取本地缓存,缓存不命中则到统一缓存层获取。统一接入层按原有逻辑处理请求,缓存不命中则回源到服务器端获取数据。同时,统一接入层 Web 服务器需要能够识别用户请求是 CDN 回源类型,还是正常请求,以免重复打点访问日志和 GZIP 压缩。

        缓存失效

        缓存失效原理与统一接入层类似。失效执行流程大致为,客户端请求经 VIP 被随机分配给失效中心某个节点,然后失效任务被发送至代理,经代理向缓存服务器发送失效命令并返回结果,如图 8 所示。

天猫浏览型应用的CDN静态化架构演变

图 8  缓存失效原理

        动态内容填充

        业务方面,因为存在定时切换页面局部内容的需求,整体架构中增加 ESI 和页面打点作为动态内容填充方式。ESI 标签由 Cache 层负责解析回源,并且会对 ESI 请求做缓存,并且提供如下特性。

  • 需要定时做全站变更的页面模块用 ESI 的 Include 实现,时间判断则放在应用服务器处理回源请求的时候。
  • 回源以后,应用服务器设置失效时间。例如请求回源时应用服务器加上s-maxAge,这个页头的缓存在定点失效。
  • Cache 系统提供合并回源,避免重复,防止失效后的高并发回源给应用服务器带来冲击。
  • Cache 系统在 ESI 的缓存失效后回源,回源的请求处理期间不会挂起外部请求,会继续向客户端返回老版本的页面,回源请求处理完以后更新成新版本。类似 Copy on Write,防止回源请求挂起导致前端服务器挂起。
  • ESI 回源时对 Response Header 的操作不会发到客户端。

        改造效果

        最终基于 CDN 静态化的架构去除了单机缓存的横向扩展瓶颈,命中率越高、系统容量越大的特性决定了可以用较小的成本支持峰值流量;引入 ESI 编程模型,解决了页面上的局部刷新问题,支持双 11 业务中一些需要全网定时切换页面内容的特殊需求;静态页面+弱依赖改造带来高可用性,并最终沉淀出了一套与应用无关的缓存和失效体系。2013 年双 11 当天,凭借这一整套 CDN 静态化架构,天猫商品详情等浏览型系统平稳度过了创造历史的一天,无论是页面访问量(PV)还是页面请求峰值(QPS)均创新高,而系统本身非常稳定,并 有充足余量承受更大级别的访问流量。同时,新的部署模型和基于 CDN 节点地域特性的缓存体系, 也降低了秒级请求的冲击型峰值,更好地满足了系统稳定性需求。在未来一段时间内,与天猫类似的浏览型系统均能够参照这套架构体系较为方便地完成静态化改造 和接入,并达到理想的稳定性和可伸缩目标。

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

来自: CSDN