深刻浅出Node.js(八):Connect模块解析(之二)静态文件中间件

上一篇专栏简单介绍了Connect模块的基本架构,它的执行模型十分简单,中间件机制也使得它十分易于扩展,具有良好的可伸缩性。在Connect的良好机制下,咱们本章开始将逐步解开Connect生态圈中中间件部分,这部分给予Connect良好的功能扩展。 html

静态文件中间件

也许你还记得我曾经写过的Node.js静态文件服务器实战,那篇文章中我叙述了如何利用Node.js实现一个静态文件服务器的许多技术细节,包括路由实现,MIME,缓存控制,传输压缩,安全、欢迎页、断点续传等。可是这里咱们不须要去亲自处理细节,Connectstatic中间件为咱们提供上述全部功能。代码只需寥寥3行便可: 前端

var connect = require('connect');  
var app = connect(); 
app.use(connect.static(__dirname + '/public'));

在项目中须要临时搭建静态服务器,也无需安装apache之类的服务器,经过NPM安装Connect以后,三行代码便可解决需求。
这里须要说起的是在使用该模块的一点性能相关的细节。 node

动静分离

前一章说起,app.use()方法在没有指定路由信息时,至关于app.use("/", middleware)。这意味着静态文件中间件将会在处理全部路径的请求。在动静态请求混杂的场景下,静态中间件会在动态请求时也调用fs.stat来检测文件系统是否存在静态文件。这形成了没必要要的系统调用,使得性能下降。
解决影响性能的方法既是动静分离。利用路由检测,避免没必要要的系统调用,能够有效下降对动态请求的性能影响。 git

app.use('/public', connect.static(__dirname + '/public'));

在大型的应用中,动静分离一般无需到一个Node.js实例中进行,CDN的方式直接在域名上将请求分离。小型应用中,适当的进行动静分离便可避免没必要要的性能损耗。 github

缓存策略

缓存策略包含客户端和服务端两个部分。
客户端的缓存,主要是利用浏览器对HTTP协议响应头中cache-controlexpires字段的支持。浏览器在获得明确的相应头后,会将文件缓存在本地,依据cache-controlexpires的值进行相应的过时策略。这使得重复访问的过程当中,浏览器能够从本地缓存中读取文件,而无需从网络读取文件,提高加载速度,也能够下降对服务器的压力。
默认状况下静态中间件的最大缓存时设置为0,意味着它在浏览器关闭后就被清除。这显然不是咱们所指望的结果。除非是在开发环境能够无视maxAge的设置外,生产环境请务必设置缓存,由于它能有效节省网络带宽。 算法

app.use('/public', connect.static(__dirname + '/public', {maxAge: 86400000}));

maxAge选项的单位为毫秒。YUI3的CDN服务器设置过时时间为10年,是一个值得参考的值。
静态文件若是在客户端被缓存,在须要清除缓存的时候,又该如何清除呢?这里的实现方法较多,一种较为推荐的作法是为文件进行md5处理。 apache

http://some.url/some.js?md5

当文件内容产生改变时,md5值也将发生改变,浏览器根据URL的不一样会从新获取静态文件。md5的方式能够避免没必要要的缓存清除,也能精确清除缓存。
因为浏览器自己缓存容量的限制,尽管咱们可能设置了10年的过时时间,可是也许两天以后就被新的静态文件挤出了本地缓存。这将持续引发静态服务器的响应,也即意味着,客户端缓存并不能彻底解决下降服务器压力的问题。
为了解决静态服务器重复读取磁盘形成的压力,这里须要引出第二个相关的中间件:staticCache后端

app.use(connect.staticCache());  
app.use(“/public”, connect.static(__dirname + '/public', {maxAge: 86400000}));

这是一个提供上层缓存功能的中间件,可以将磁盘中的文件加载到内存中,以提升响应速度和提升性能。
它的官方测试数据以下: 浏览器

static(): 2700 rps 
node-static: 5300 rps 
static() + staticCache(): 7500 rps

另外一个专门用于静态文件托管的模块叫node-static,其性能是Connect静态文件中间件的效率的两倍。可是在缓存中间件的协助下,能够弥补性能损失。
事实上,这个中间件在生产环境下并不推荐被使用,并且它将在Connect 3.0版本中被移除。可是它的实现中有值得玩味的地方,这有助于咱们认识Node.js模型的优缺点。
staticCache中间件有两个主要的选项:maxObjectsmaxLength。表明的是能存储多少个文件和单个文件的最大尺寸,其默认值为128和256kb。为什么会有这两个选项的设定,缘由在于V8有内存限制的缘由,做为缓存,若是没有良好的过时策略,缓存将会无限增长,直到内存溢出。设置存储数量和单个文件大小后,能够有效抑制缓存区的大小。
事实上,该缓存还存在的缺陷是单机状况下,一般为了有效利用CPU,Node.js实例并不仅有一个,多个实例进程之间将会存在冗余的缓存占用,这对于内存使用而言是浪费的。
除此以外,V8的垃圾回收机制是暂停JavaScript线程执行,经过扫描的方式决定是否回收对象。若是缓存对象过大,键太多,则扫描的时间会增长,会引发JavaScript响应业务逻辑的速度变慢。
可是这个模块并不是没有存在的意义,上述说起的缺陷大多都是V8内存限制和Node.js单线程的缘由。解决该问题的方式则变得明了。
风险转移是Node.js中经常使用于解决资源不足问题的方式,尤为是内存方面的问题。将缓存点,从Node.js实例进程中转移到第三方成熟的缓存中去便可。这能够保证: 缓存

  1. 缓存内容不冗余。
  2. 集中式缓存,减小不一致性的发生。
  3. 缓存的算法更优秀以保持较高的命中率。
  4. 让Node.js保持轻量,以解决它更擅长的问题。

Connect推荐服务器端缓存采用varnish这样的成熟缓存代理。而笔者目前的项目则是经过Redis来完成后端缓存的任务。

参考内容

关于做者

田永强,新浪微博@朴灵,前端工程师,曾就任于SAP,现就任于淘宝,花名朴灵,致力于NodeJS和Mobile Web App方面的研发工做。双修先后端JavaScript,寄望将NodeJS引荐给更多的工程师。兴趣:读万卷书,行万里路。我的Github地址:http://github.com/JacksonTian。 

相关文章
相关标签/搜索