关于大型网站技术演进的思考(十二)--网站静态化处理—缓存(4)
上篇我补充了下SSI的知识,SSI是一个十分常见的技术,记得多年前我看到很多门户网站页面的后缀是.shtml,那么这就说明很多门户网站都曾经使用过SSI技术,其实现在搜狐网站也还在用shtml,如下图所示:
由此可见SSI在互联网的应用还是非常广泛的。其实互联网很多网页如果我们按照动静分离策略拆分,绝大部分都是可以当做静态资源处理,例如新闻网站,文学网站,这些网页生成后,大部分的资源都是不变的,说白了这些网页本质就是一个静态页面,我们开发他们时候也不需要服务端的参入,每一个网站都有自己固定的板式,假如每个新网页都要完完整整的开发,重复性的工作实在太多了,出错的概率也非常高,在本系列第二篇里我曾经详细介绍了velocity的布局模板技术,其实SSI也可以制作出一套固定的模板,开发时候我们只需要在定义好的模板里添加或者修改我们需要更改的内容就可以完成一个页面的制作,可见SSI技术为了我们开发网页提供了很大的便利。
与SSI相对应的还有ESI,这个技术不是太常用,在网上能收集到的资料也不是太多,网上有限的资料也基本都是和淘宝相关的,不过仔细研究下淘宝对ESI的运用,对于理解网站静态化处理是非常有借鉴意义的,下面我将重点讲讲ESI的知识。首先看个场景啊。
我们登录支付宝网站,到了个人首页我们发现在支付宝下面有一个条目,如下图所示:
这是支付宝默认给我们显示的【生活好助手】,右边有个箭头,我们点开它,如下图所示:
我们看到这里有添加的按钮,通过添加按钮,我们可以添加其他常用的组件图标。(注意:我这里只是以支付宝这个功能为例,支付宝是否按照我说的设计,这个我就不清楚了)
如果我们按照自己个性化添加了自己的组件,不同的人添加的常用组件也会是不尽相同的,如果我们自己开发的网站也有这样的功能,那么我们该如何设计了?我们一般都会很直观的把这个新增的组件信息存储在数据库里,用户每次登录时候该信息也会随之从数据库里读取,但是这个场景对于像支付宝这种用户量极大,日均访问量极高的大型网站,这种个性化又非核心的功能都从数据库里读取,那么它对数据库造成的压力一定是十分巨大的,在存储的瓶颈里我们讲了那么多优化数据库的手段,其核心手段有一个就是减少对数据库价值不高的操作,而这种个性化配置跟支付宝的支付操作相比起来,价值度实在太低,因此我们最好的选择是避免数据库承担过多此类的操作。
不过像上面这个场景里的功能,它所使用的数据又不是那种可有可无的,假如数据存储的不可靠造成数据丢失还是会造成不必要的麻烦,所以我们还是会把这些信息做持久化存储。此外像上面的【生活好助手】条目还是页面的一个重要组成部分,因此像SSI那种使用html注释指令,当指令无法正常解析,就直接返回到浏览器,因为是注释,所以页面也不会显示它,SSI的这种做法用在上面场景肯定是不太合适的。这样的场景在电商网站里是十分常见的,例如一个商品页面,页面里会有商品的图片,还有商品的详细介绍,这些内容其实都是会用持久化系统进行存储,同时它们本身也是网页的重要做成部分,如果碰到问题就忽略最终会造成页面显示错误。
结合上面的场景我们来讨论下ESI技术了,ESI技术和SSI技术类似,其实也和jsp里的include指令类似,它也是在页面里使用一个指令标签web容器解析这个标签后将获取的数据替换掉这个标签。我们来看看ESI的使用方法,我们可以在velocity里自定义一个esi标签,velocity里的使用如下所示:
esiTool.setTemplate('test.vm').addQueryData('id', 100)
velocity引擎解析vm模板,最终会把vm解析成html页面,这个时候该页面里使用esi标签的地方就被转化为:
<esi:include src="test.vm.esi?id=100" />
当页面到了服务端web容器之前的静态web容器(该web容器要安装好解析esi的模块),静态web容器就会解析这个esi标签,静态web容器会以test.vm.esi?id=100 作为key,到缓存系统里查找信息,如果查到了信息,缓存服务器就直接返回,用返回内容替换掉esi标签,如果缓存里没有找到则会直接请求持久化系统,持久化系统返回信息后,缓存系统将信息缓存起来,同时也将信息返回至静态web容器,那么下次用户再访问同样内容就会直接从缓存里读取了。
ESI技术和SSI很像,只不过ESI技术是和缓存技术配合起来的,同时ESI标签也不像SSI标签那样使用注释的形式,因此ESI标签是一定要被解析的,如果仅仅是缓存,ESI和SSI比较起来也没显得那么有优势和特别,但是对于电商这种场景而言ESI的现实意义非常大,电商网站也是一个由用户产生内容的网站,每一个商家的店铺虽然我们都知道它是属于淘宝或天猫的,但是单独一个商家的店铺都是个性化很强的,与其他店铺差异很大,为了买卖商品,商家会上传自己商品的图片,还会使用图片和文字描述自己的商品,单个商品页面我们做动静分离分析,很容易分辨出动态内容和静态内容,但是如果一个电商平台拥有超乎想象数量的商家,那么每个页面的图片,文字和商家页面的关系就会变得有点微妙了。由于电商网站的图片特别多,那么电商网站系统一般都会设计一套管理这些小图片的分布式存储系统,例如淘宝的TFS文件系统,它是专门针对图片使用的分布式文件系统,这些文件系统里存储的图片会和商家紧密关联,这就让图片本身拥有了一定的动态属性,但是对于每个商家页面而言,商家自己的图片资源都是可以静态化的,也就是说图片的读取是要通过商家信息进行计算的,计算出的结果对于商家而言又是静态的,可以被缓存的。但是这个静态资源的处理时候就变得复杂了,而这些是SSI无法完成的。
首先我们直接从文件系统读取图片,效率是非常低效,因此我们还是会把它们缓存在内存里,但是由于不同图片和不同商户是相关联,那么对于缓存查找时候是需要一定的条件,不同商户对自己页面的设计方案也会有所不同,一般商户这些资源,存储系统肯定会按照设计模板的维度存储,不同商家由于商品和文化的不一样,那么使用的模板也不一样,因此资源返回静态web容器前还需要一个整合过程,这样场景下的静态资源获取其实是需要一定逻辑计算的,那么这个计算一般都会在开发时候由代码完成,所以从上面ESI使用的例子看到,开发人员会使用velocity的esi标签,这个标签开发人员可以设置参数,velocity引擎最终会将这个标签解析成静态web容器可以解析的esi标签,标签里有这样的代码test.vm.esi?id=100,文件后面会带上参数,那么这个参数其实是动态的,那么这个参数也就是缓存系统获取正确信息的规则了。这样我们就完成了静态资源获取的逻辑计算,计算完毕后这些资源会在一段时间里长期有效,因此它就演变为静态资源,可以被缓存了。ESI比SSI强大多了,同时ESI也可以完成SSI的功能,所以使用了ESI也就没必要用SSI了。
像我们平时做web开发时候可能没有太留心一个问题,一般的web开发里使用的静态资源例如图片,css文件,js文件我们都会放置在一个resource包里,如果是企业开发,这个web应用上线时候也就直接打包在web工程里,一些互联网网站也只不过会将这些资源放置在单独的静态服务器上,我平时开发时常听到有人说,项目里图片太多了,应该合并下,css文件和js文件也太多了也要合并下,这个多到底多多少了,几十个文件,几百个文件,这个要和社交网站,电商网站这种用户可以产生图片的网站比起来那就是小巫见大巫了,因为用户能产生内容的网站静态资源会随着时间推移文件规模变得异乎寻常的大,所以此类网站的静态资源已经没法放置在项目下,它就要求我们需要有新的手段管理这些静态资源,并且有新的手段使用这些静态资源,那么像TFS文件存储系统出现了,缓存技术出现了,最后我们在应用里使用ESI技术把它们整合到我们网页里,通过这个分析我们就能明白ESI适用的业务场景了。
网站静态化处理我们首先要按规则拆分动静资源,拆分出来的静态资源该如何处理就是网站静态化处理的关键所在,把静态资源处理从服务端的web应用里剥离出来,不让服务端的web应用参入过多的静态资源解析,这样就可以为服务端的web应用减少不必要的处理操作,从而达到提升服务端web应用的运行效率,接着我们就把拆分出来的静态资源处理操作往前推移到静态web服务器,前两篇文章和今天的文章我着重讲解了静态web服务器处理静态资源的手段,那么这里有个问题了,这些处理可以再往前推到浏览器来完成吗?答案当然是否定的,首先浏览器的缓存是非常不可靠的,如果用户把浏览器设置为不缓存任何数据的模式,那么浏览器就没法缓存数据了,而用户的行为那是根本没法控制的,其次浏览器缓存的数据量是有限的,如果我们要在浏览器进行缓存也是缓存最有价值缓存的数据,更重要的一点,为了做好网站静态化处理我们对网页的动静资源做了拆分,但是拆分出的静态资源也并不是完全不需要进行任何逻辑处理就能使用的,例如前面讲到的ESI适用的场景我们就发现,有些静态资源的获取还是要很多条件的参入,而这些条件是由动态数据产生的,那么这样的静态数据浏览器是没法做缓存的,这点也说明了拆分出来的静态化资源绝大部分还是要停留在服务端的,居然只能停留在服务端,那么最为高效的处理这些静态资源的地方就是CDN和静态web容器了。所以在本系列的第一篇里我讲到网站生产部署时候最好是在服务端web应用之前放置一个静态web容器,如果有了这样的静态web容器做反向代理,那么我们就可以让它来完成静态资源的相关操作,而且静态web容器还能辅助完成一些逻辑上的处理,从而弥补了CDN的不足之处。当然这么做的好处不仅仅只有这些,第二篇文章里我曾经讨论了反向代理的好处,可能大家印象还不是很深刻,我将会在后面文章里对反向代理做更加深入的分析。
讲完了ESI的妙用后,我下面将讲讲本篇的主题缓存了。其实单独讲缓存真的没啥太多内容,不过在网站静态化处理里的缓存还是和存储里讲到的分布式缓存有所不同,分布式缓存的数据都是存储在内存里,而网站静态处理的缓存既有存储在内存里还有存储在硬盘上,当然存储在内存里读取速度会更快,但是网站的读取效率还和资源距离用户的远近有关系,例如浏览器的缓存其实是把静态内容缓存在硬盘上,但是因为不需要通过网络获取资源,因此它的读取效率就显得特别高了,除了这个因素外还有个因素,我们前面做动静拆分的目的就是想拆分出静态资源,让这些静态资源远离服务端的web应用,这样可以减少服务端不必要的压力,从而达到提升服务端web应用处理能力,而把静态资源放置在静态web容器处理,它的处理静态资源效率又会高于在服务端web应用的处理,从而也达到提升静态资源读取的效率。不过如果静态资源最终只能放置在服务端,那么这个时候我们把静态资源存入到缓存里即内存里效率肯定比在硬盘上高,所以CDN的服务节点一般都是采取将静态资源存储在内存里,就算是服务端web应用前的静态web容器,如果我们让静态资源缓存在内存里,效率肯定也是比在硬盘上高。
好了,今天就写到这里,最后祝大家生活愉快,新年快乐。