转:RoR网站如何利用lighttpd的X-sendfile功能提升文件下载性能

转载 2011年06月05日 13:12:00

http://www.iteye.com/topic/154538

 

传统的Web服务器在处理文件下载的时候,总是先读入文件内容到应用程序内存,然后再把内存当中的内容发送给客户端浏览器。这种方式在应付当今大负载网 站,音频视频网站力不从心。sendfile是现代操作系统支持的一种高性能网络IO方式,操作系统内核的sendfile调用可以将文件内容直接推送到 网卡的buffer当中,从而避免了Web服务器读写文件的开销,实现了“零拷贝”模式。

作为最流行的轻量级Web服务器的翘楚,lighttpd提供了良好的sendfile支持,JavaEye网站服务器使用的就是 lighttpd。在Linux操作系统上面,只需要在lighttpd.conf配置文件如下配置,lighttpd就会使用sendfile方式处理 静态资源的下载,效率非常高:

引用
server.network-backend = "linux-sendfile"



但是在某些情况下,我们却无法直接让lighttpd处理文件的下载,比方说JavaEye网站需要统计帖子附件的下载次数,博客相册的点击次 数,比方说需要对下载的文件进行权限的控制,特别是对于一些多用户系统,你不能让用户上传的私密文件被其他用户随便下载到,例如JavaEye圈子的共享 文件不能够对圈子外的用户开放下载。因此,文件下载目录千万不能放到public目录下,不能让用户直接通过浏览器的URL地址访问到。在这种情况下,文 件下载必须由服务器端应用程序来处理。

在RoR应用当中,我们可以在controller中使用send_file方法来控制文件的下载。send_file方法将下载的文件以4KB 为单位写到一个输出流去。如果我们使用mongrel应用服务器的话,mongrel会在内存当中创建一个StringIO对象,把整个下载文件完整的读 入内存,然后再向客户端或者前端的Web服务器写出。如果我们使用fcgi来运行RoR的话,fcgi会直接把输出流的内容向前端的Web服务器写出。

毫无疑问,我们可以看到这种下载处理方式有很大的性能缺陷:

1、当使用mongrel的时候,如果下载文件很大,会导致mongrel内存暴涨!

mongrel创建一个StringIO对象缓存整个输出内容,我们假设用户下载的是一个100MB的文件,该用户又很喜欢用多线程下载工具,他 开了10个线程并发下载,那么mongrel的内存占用会暴涨1GB以上。而且最可怕的是,即使当用户下载结束以后,mongrel的内存都不会迅速回 落,而是一直保持如此高的内存占用。这个缺陷非常容易被别有用心的黑客利用,攻击网站。这也是JavaEye网站为什么始终不用mongrel的原因之 一。


2、当使用fcgi的时候,如果前端Web服务器没有足够大buffer,会导致fcgi进程被挂住

fcgi自己不开output buffer,而是实时写出输出内容,如果前端Web服务器用的是lighttpd,那么你很幸运,lighttpd会照单全收,一个字节都不拉下;如果 前端Web服务器用的是nginx/apache,那么你很不幸,nginx/apache默认只开8K的buffer,收不下的那就对不起了,您慢点 嘞,fcgi进程就被挂住了,只要客户端浏览器下载不结束,fcgi进程就被一直占用。

3、即使使用lighttpd+fcgi,也会对服务器造成不小的性能开销

lighttpd+fcgi是最理想的Rails部署环境,JavaEye网站使用的就是lighttpd+fcgi。当ruby程序执行 send_file开始下载的时候,fcgi会以4KB为单位读入文件内容,然后立刻写出到lighttpd去,而lighttpd照单全收。因此当下载 文件被完整的通过fcgi被flush到lighttpd的内存里面去以后,即使你杀掉fcgi进程,都丝毫不会影响文件下载。

也许你会问,lighttpd都吃下来文件内容,内存会不会暴涨?会的,我们假设同样的用户场景,某用户启动10个线程下载100MB的文 件,fcgi进程内存不会发生变化,但是lighttpd会暴涨1GB。但所幸的是lighttpd的内存管理的不错,一旦用户取消下载,或者下载完 毕,lighttpd立刻释放掉1GB的内存。

但是无论怎么说,ruby还是需要完整的读取下载文件,而lighttpd也需要开辟足够大的内存,处理整个文件的下载过程,对服务器开销还是很 大的。我们的问题是,能不能让带权限控制的文件下载像lighttpd下载静态资源文件那样快,开销那样小呢?答案就是X-sendfile!

使用X-sendfile方式,服务器端应用程序不需要读取下载文件了,只需要设置response的header信息就足够了,此外还要附加一 个信息“X-LIGHTTPD-send-file”信息给lighttpd,告诉lighttpd,文件下载我就不管了,你自己看着办吧:

Ruby代码  收藏代码
  1. response.headers[ 'Content-Type' ] =  @attachment .content_type  
  2. response.headers['Content-Disposition' ] =  "attachment; filename=/"#{URI.encode(@attachment.filename)}/""    
  3. response.headers['Content-Length' ] =  @attachment .size  
  4. response.headers["X-LIGHTTPD-send-file" ] =  @attachment .public_filename  
  5. render :nothing  =>  true   



X-LIGHTTPD-send-file告诉lighttpd,去硬盘的哪个路径找要下载的文件,最后一行啥都不输出了,下载不用ruby来管了。

而lighttpd收到X-LIGHTTPD-send-file信息以后,就会找到硬盘该文件,以静态资源文件的下载方式处理,丝毫不消耗 lighttpd的内存。还是以某用户启动10个线程下载100MB文件为例,10个fcgi进程发送了response信息就处理完毕了,而 lighttpd知道下载的是硬盘的静态文件,会以sendfile方式下载,文件内容就会被操作系统内核直接送到网卡的buffer里面,既不消耗 ruby进程,也不消耗lighttpd,皆大欢喜。

在lighttpd-1.4.18版本里面,fastcgi方式已经内置X-sendfile支持,仅仅需要你在配置文件打开就可以了:

引用
"allow-x-send-file"="enable"



JavaEye网站在使用了X-sendfile功能之后,lighttpd的内存占用有明显的下降。未使用X-sendfile之 前,lighttpd有时候内存占用会到200MB以上(有用户多线程下载附件),在使用X-sendfile之后,lighttpd的内存占用还从未突 破20MB。

最后要提醒大家几个问题:

1、lighttpd-1.4.x不认X-sendfile这个header,只认X-LIGHTTPD-send-file

按照lighttpd网站自己的文档,以及各种各样流行的X-sendfile文档,设置的header都是X-sendfile,但是经过我们 n次失败的摸索,才发现原来必须使用X-LIGHTTPD-send-file,这一点请不要被文档迷惑,目前好像也只有我们提出这个解决办法,互联网上 面尚未看到其他人提出过,看来我们又首开先河了。用RoR就是这点好,你动不动就得自己先去当尝螃蟹的那个人。

2、lighttpd-1.5.0版本的X-sendfile设置有所改变

lighttpd-1.5.0版本还未发布正式版本,据说1.5.0已经认识X-sendfile这个header了,这个大家有兴趣自己测试吧。

 

补充几句:

1、lighttpd-1.4.18仅支持fastcgi情况下的X-sendfile,而1.5.0版本则支持各种情况包括fastcgi, scgi, http proxy, ajp等协议下的X-sendfile

2、apache不支持X-sendfile,可以通过第三方mod添加支持,但据说不太好用

3、nginx有类似lighttpd的X-sendfile,叫做X-Accel-Redirect,但是使用的限制比较大,必须实现在 nginx上面配置到某个目录下的文件才具备这样的功能,而不像lighttpd,任意目录文件不需要配置都可以支持,因而很受人批评。

当然,我还是推荐大家使用lighttpd+fcgi,这是性能最佳Rails方案,我会另外写文章剖析Rails部署方案的优劣。

相关文章推荐

RoR网站如何利用lighttpd的X-sendfile功能提升文件下载性能

传统的Web服务器在处理文件下载的时候,总是先读入文件内容到应用程序内存,然后再把内存当中的内容发送给客户端浏览器。这种方式在应付当今大负载网站,音频视频网站力不从心。sendfile是现代操作系统支...
  • kaylc
  • kaylc
  • 2011年07月15日 13:31
  • 424

Apache,Nginx,Lighttpd分别使用X-sendfile功能提升文件下载性能

关于mod_xsendfilehttps://tn123.org/mod_xsendfile/ Lighttpd中的X-sendfileRoR网站如何利用lighttpd的X-sendfile功能提升...

使用 Nginx 的 X-Sendfile 机制提升 PHP 文件下载性能

很多时候用户需要从网站下载文件,如果文件是可以通过一个固定链接公开获取的,那么我们只需将文件存放到 webroot 下的目录里就好。但大多数情况下,我们需要做权限控制,例如下载 PDF 账单,又例如下...

使用 sendfile() 提升网络文件发送性能

原文见:http://www.linuxjournal.com/article/6345 文章中列出了我们平时通过网络发送文件时会用到的两个系统调用: read(file, tmp_buf, le...

sendfile原理,实现文件传输性能的提升

在apache,nginx,lighttpd等web服务器当中,都有一项sendfile相关的配置,在一些网上的资料都有谈到sendfile会提升文件传输性能,那sendfile到底是什么呢?它的原理...

sendfile原理,实现文件传输性能的提升

在apache,nginx,lighttpd等web服务器当中,都有一项sendfile相关的配置,在一些网上的资料都有谈到sendfile会提升文件传输性能,那sendfile到底是什么呢?它的原理...
  • wm_1991
  • wm_1991
  • 2016年07月15日 11:16
  • 1668

PHP X-sendfile实现文件下载

项目中涉及到文件的下载功能,通常PHP文件下载都是将文件先读取出来然后发送相应头文件进行下载。 如:$file_dir = $instance->_attach_path.Helper_Hash::h...

使用 Nginx 的 X-Sendfile 机制控制文件访问权限(php版)

BBS或者网站经常会有只有你有权限才能看到这个页面,或者下载这个资源,日前探访了nginx服务器怎么实现这个文件控制的,用到了x-sendfile。 一、什么是 X-Sendfile? X...

使用sendfile() 提高网络文件发送性能

我们平时通过网络发送文件时会用到的两个系统调用: read(file, tmp_buf, len); write(socket, tmp_buf, len); 调用过程示意图如下: ...

使用sendfile提升性能

摘自:http://hily.me/blog/2011/01/use-sendfile-accelerate-file-sending/ 英语原文见:http://www.linuxjournal....
  • kl222
  • kl222
  • 2012年09月16日 17:33
  • 3575
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:转:RoR网站如何利用lighttpd的X-sendfile功能提升文件下载性能
举报原因:
原因补充:

(最多只允许输入30个字)