Nginx篇06——Sendfile指令及其原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx篇06——Sendfile指令及其原理相关的知识,希望对你有一定的参考价值。

参考技术A nginx中http模块中的sendfile指令及其原理。

nginx的http模块中有一个 sendfile 指令,默认是开启状态, 官网的文档 对其解释是:

简单来说就是启用sendfile()系统调用来替换read()和write()调用,减少系统上下文切换从而提高性能,当 nginx 是静态文件服务器时,能极大提高nginx的性能表现,而当 nginx 是反向代理服务器时,则没什么用了。下面我们来分析一下这个sendfile的工作原理:

首先我们需要知道sendfile()和read()、write()之间最大的区别就是前者是属于系统调用,而后者是属于函数调用,我们来看下面这幅图

我们不难看出,nginx是属于Applicaiton的,而read()、write()属于函数调用,也就是在Lib Func这一层,sendfile()系统调用,位于System Call这一层,而想要对硬盘进行操作,是kernel才有的权限,上面的那些层都需要往下调用。

作为对比我们先来看一下正常情况下如果nginx调用read()和write()函数的操作过程:

我们都知道数据是存储在硬盘上面的,当数据被调用的时候会被加载进内存再被层层递进最后被CPU使用,这里个这个过程我们进行简化。

这里需要说明两点,一是用户态和内核态之间的切换是需要执行上下文切换操作的,这是十分耗费系统资源和时间的操作,二是因为read()、write()属于函数调用,它们是没有权限在kernel中操作,无法将data直接从Kernel Buffer(Hard Disk)传输到Kernel Buffer(Socket Engine)。

那么使用sendfile()呢?,由于是系统调用,所以在步骤二和步骤三的时候就可以不需要再将数据传输到User Buffer,直接在kernel中进行操作,省去了两次状态切换,也就是省去了两次的上下文切换,从而大幅度提升了性能。

我们来看一下下面的这幅图(灵魂画手上线→_→)

最后我们再来解释一下,为什么当 nginx 是反向代理服务器时, sendfile() 就没什么用了呢。

顾名思义, sendfile() 的作用是发送文件,也就是接收数据的一段是文件句柄,发送数据的那一端是socket。而在做反向代理服务器的时候,两端都是socket,此时无法使用 sendfile() ,也就不存在性能提升这一说了。

通过 Rails 2.3 使用 x-sendfile 通过 Nginx 提供大文件

【中文标题】通过 Rails 2.3 使用 x-sendfile 通过 Nginx 提供大文件【英文标题】:Serving Large Files Through Nginx via Rails 2.3 Using x-sendfile 【发布时间】:2010-10-31 13:36:45 【问题描述】:

假设我有一个 Rails 2.3.2 应用程序,前面是 nginx,由 mongrel 提供服务,我需要通过 Rails 提供一个大型静态文件(以控制对它的访问)。我希望 Rails 应用程序将文件传输委托给 nginx,以避免阻塞 mongrel 实例。

现有信息似乎相互矛盾且不完整。 This post 展示了如何使用 Apache 来完成,并暗示它也可以使用 ngninx 来完成 - 但没有示例。 This post 和 this post 展示了如何使用 Rails 2.3 显然不需要的插件来做到这一点。 This post 建议可能毕竟 nginx 不支持 x-sendfile。

我宁愿不为 Rails 现在可以自己做的事情使用插件。

有没有人在不使用插件和 Rails 2.3/nginx/mongrel 的情况下获得类似 x-sendfile 的行为?如果没有,让它与插件(和/或猴子补丁)和 Rails 2.3/nginx/mongrel 一起工作的最佳文档是什么?

【问题讨论】:

【参考方案1】:

主要思想:您的控制器所做的就是设置 nginx x-accel-redirect 标头。一旦您的控制器方法返回(这将非常快),nginx 将查看您的 Rails 应用程序集的标题。如果设置了 x-accel-redirect,则 nginx 提供静态文件。

您的控制器将如下所示:

def show  
  @attachment = Attachment.find(params[:id])  
  # Do anything else you need for authentication, etc. 

  head(:x_accel_redirect => '/files/' + @attachment.filename,  
   :content_type => @attachment.content_type,  
   :content_disposition => "attachment; filename=\"#@attachment.filename\"")  
end  

仅此一项并不能解决问题。您还需要告诉 nginx 位于 $RAILS_ROOT/files 的文件。将此添加到服务器块内 nginx 配置的末尾:

location /files 
  root /path/to/rails_app;  
  internal;  

将静态文件放入 $RAILS_ROOT/files 中,它应该可以工作。无需插件或猴子补丁 使用 Rails 2.3.2 和 2.3.14 测试。

【讨论】:

是的,就是这样。请注意,nginx conf 中的 que "root" 必须是该位置的父节点。 这似乎有点老了。这仍然适用于较新版本的 rails 吗?

以上是关于Nginx篇06——Sendfile指令及其原理的主要内容,如果未能解决你的问题,请参考以下文章

实验3 转移指令跳转原理及其简单应用编程

Nginx静态资源部署

nginx中配置sendfile及详细说明

nginx (linux)sendfile 参数解释

nginx (linux)sendfile 参数解释

Linux-Nginx之sendfile与上下文切换