本篇文章着重整理什么是静态化系统架构,以及静态化架构的几种方案。
首先先贴一个淘宝系统升级的历程(09~13):
- 09年,系统拆分,静态文件合并,前端页面异步化和json化。
- 10年,去DB依赖、引入缓存、提升单机QPS、关注用户体验。
- 11年,优化进入深水区Velocity、BigPipe。
- 12年,静态化改造。
- 13年统一Cache、CDN化、网络协议。
一、什么是静态化架构,为什么需要它
静态化架构系统通常有几个特征:
- 一个URL标识一个唯一的页面。
- 页面不包含cookie等私有数据,即用户信息,时间,地点等。
当出现高并发问题,一般情况下首先想到的是优化java程序,但java程序本身是有瓶颈的,并且他也存在不擅长处理大量连接请求,每个连接消耗内存较多,Servlet容器解析HTTP较慢等问题,那要怎样跳出Java来进行优化了?
以下是静态话化架构的处理方式:
- 首先分离页面静态内容,页面内容并不是所有都需要从后台获取,将必须后台处理的分离出去,留下不受用户信息影响的静态的页面内容。
- 改变静态内容缓存的位置,不再缓存到java层面,直接在Web服务器上做缓存(Nginx,Apache,Varnish);
- 分离出的动态内容Json化,以便复用。获取动态内容有两种方式ESI、CSI
- ESI:在Web服务器上做动态内容请求,并将动态内容插入到静态页面,用户拿到的是一个完整的页面,用户体验好,服务器压力较大。
- CSI:通过JS直接发送动态内容请求,用户体验差,服务器压力小。
二、静态化架构的几种方案
首先是静态化方案需要考虑的几个问题:
- 是否一致性Hash分组?做缓存一定是和命中率紧密相关的,命中率和数据的集中度相关,而要让数据集中一致性Hash就是一个必然选择。但是一致性Hash有一个天然的缺陷就是会导致热点问题,当热点特别集中时可能会导致网络瓶颈。
- 是否使用ESI?ESI对性能有影响,但是他对客户端友好,前端编程也方便。
- 是否使用物理机?物理机可以提供更大的内存、更好的CPU资源,但是使用物理机也有一些缺点,例如会导致应用集群的相对集中,进而导致网络风险增加。另外对Java系统而言内存增加并不能带来那么大的好处。
- 谁来压缩、在哪里压缩也是让人比较纠结的问题,增加一层Cache,必然增加了数据的传输,那么谁来压缩就会影响到Cache的容量和网络数据的传输量就。
- 网卡选择?网卡选择其实是个成本问题,避免网络瓶颈可以选择万兆网卡和交换机,但是必然成本增加。
方案1.采用Nginx+Cache+Java结构的虚拟机单机部署
这种方案是最简单的静态化方案,只需要在静态化改造的基础上,在前面的架构上加一层Cach就行,他的优缺点如下:
优点:
- 没有网络瓶颈,不需要改造网络;
- 机器增加,也没有网卡瓶颈;
- 机器数增多,故障风险减少。
缺点:
- 机器增加,缓存命中率下降;
- 缓存分散,失效功能难度增加;
- Cache和JBoss都会争抢内存;
该方案虽然比较简单,但也能够解决热点商品的访问问题,例如做大促时,商品数比较少,在有限内存中任然能够命中这些商品;另外针对一些恶意攻击也十分有效,这是的命中率能达到90%以上,但是对系统的整体性能没有很多提升。
方案2.采用Nginx+Cache+Java结构实体机单机部署
这种方案在前面的基础上将虚拟机改成实体机,增大Cache的内存,并且采用了一致性Hash分组的方式来提升命中率,这里将Cache分成若干组,这样就可以达到命中率和访问热点的平衡。
优点:
- 既没有网络瓶颈,也能使用大内存;
- 减少Varnish机器,提升命中率;
- 提升命中率,能减少Gzip压缩;
- 减少Cache失效的压力。
这是一个比较理想的方案,在正常请求下也能达到50%左右的命中率,对一些基数数据比较小的系统如天猫Detail,命中率能达到80%左右,这样的命中率比较理想。
方案3.统一Cache层
统一Cache层是个更理想的推广方案。
将Cache层单独拿出来统一管理可以减少运维成本,同事也方便其他静态化系统接入。
优点:
- 可以减少多个应用接入Cache的成本,接入的应用只维护自己的Java系统就好,不用单独维护Cache,只需关心如何使用,更好地让更多流量型系统接入使用。
- 统一Cache易于维护,例如后面加强监控、配置的自动化,统一维护、升级比较方便。
- 可以共享内存,最大化利用内存,不同系统之间的内存可以动态切换,有效应对攻击情况。
- 更有助于安全防护。
三、如何解决失效问题
缓存的失效同样时静态化架构需要考虑的问题,这里采用主动失效和被动失效相结合的方式。
被动失效
被动失效主要处理如模板变更和一些对时效性不是太敏感的数据的失效,采用设置Cache时间长度这种自动失效的方式,同时也要开发一个后台管理界面来用于手工失效某些Cache。
主动失效
主动有如下几种:
-
- Cache失效中心监控数据库表变化,发送Purge失效请求;
- 装修时间戳比较失效装修内容;
- Java系统发布,清空Cache;
- Vm模板发布,清空Cache;
其中主要使用第一个失效中心,这个失效中心通过监控数据库关键数据对应表的变更以发送Purge失效请求给Cache层来清除缓存数据。
四、服务端静态化方案的演进:CDN化
在静态化系统过后,还可以进一步优化,将Cache层前移到CDN(Content Delivery Network 内容分布网络,它会将用户的请求自动分配到最近的服务器进行处理,并且他会缓存网站的静态内容,加速网站的响应速度),因为CDN离用户最近,效果会更好。
但是想要CDN化,存在几个问题需要解决:
- 失效问题。由于CDN分布在全国,要在秒级时间内失效这么广泛的Cache,对CDN的失效系统要求很高。
- 命中率问题。Cache最重要的一个指标就是要保证高命中率,不然Cache就失去了意义。同样,如果将数据全部放到全国的CDN上,Cache分散是必然的,Cache分散导致访问的请求命中到同一个请求的Cache降低,那么命中率就成为一个问题。
- 发布更新问题。作为一个业务系统,每周都有日常业务需要发布,所以发布系统是否简单、快速也是一个不可回避的问题,有问题快速回滚和问题排查的简便性也是要考虑的方面。
解决失效问题
采用级联的失效结构,主动发Purge请求给Cache软件失效的方式,这种失效由失效中心将失效请求发送给每个CDN节点上的Console机,然后Concole机发送Purge请求给每台Cache机器。
解决命中率问题
现阶段把Cache放到全国所有的CDN节点上,现阶段是不太可能实现的,那么是否可以选择若干个节点来实施、尝试了?
- 这样的节点需要满足以下几个条件:
- 靠近访问量比较集中的地区;
- 离主站相对较远;
- 节点到主站的网络比较好且稳定;
- 节点容量比较大,不会占用其他CDN太多的资源;
- 节点不要太多;
基于上面几个因素,选择CDN的二级Cache比较合适。
使用CDN的二级Cache作为缓存,可以达到和前面服务端静态化Cache类似的命中率,因为节点数不多,Cache不是很分散,访问量也比较集中,这样也解决了命中率的问题,同时也提供给用户最好的访问体验,是在当前环境下比较理想的CDN化方案。
*本文是《深入分析JAVA WEB技术内幕》一书的总结和节选