sendfile: 设置为on表示启动高效传输文件的模式。sendfile可以让Nginx在传输文件时直接在磁盘和tcp socket之间传输数据。如果这个参数不开启,会先在用户空间(Nginx进程空间)申请一个buffer,用read函数把数据从磁盘读到cache,再从cache读取到用户空间的buffer,再用write函数把数据从用户空间的buffer写入到内核的buffer,最后到tcp socket。开启这个参数后可以让数据不用经过用户buffer。
Linux kernel 2.2之前,(如图)读写数据基本都是使用read系统调用和write系调用,以nginx来说如果一个请求建立,从磁盘的文件到网络连接之间会通过硬件(DMA)—内核层—用户层多次读写系统来完成文件数据的复制传输:从内核层用read系统调用读到用户层,再从用户层用write系统调用写到内核层,每一次用户层到内核层的进行一次上下文转换,这种代价是非常昂贵的。甚至在没有数据变化时这种复制尤其显得多余。如果nginx接受大量并发请求,这种系统调用就会非常频繁,服务器的性能就会下降。
| 名称 | 零复制指令 |
|---|---|
| 指令 | sendfile |
| 作用域 | http、server、location |
| 默认值 | off |
| 指令值选项 | on 或 off |
| 指令说明 | 启用零复制(sendfile)。零复制(也称零拷贝)是读取本地文件后向网络接口发送文件内容的文件传输机制 |
默认配置下,Nginx 读取本地文件后,在进行网络传输时会先将硬盘文件从硬盘中读取到 Nginx 的文件缓冲区中,操作流程为硬盘 → 内核文件缓冲区 → 应用缓冲区。然后将 Nginx 文件缓冲区的数据写入网络接口,操作流程:应用缓冲区 → 内核网络缓冲区 → 网络接口。
Nginx 的本地文件在进行网络传输的过程中,经历了上述两个操作过程,两次操作都在内核缓冲区中存储了相同的数据。为了提高文件的传输效率,内核提供了零复制技术,该技术支持文件在内核缓冲区内直接交换打开的文件句柄,无须重复复制文件内容到缓冲区,则上述两个操作的流程变为:硬盘 → 内核文件缓冲区 → 内核网络缓冲区 → 网络接口。
零复制技术减少了文件的读写次数,提升了本地文件的网络传输速度。内核缓冲区的默认大小为 4096B。
文件读取模块,它是用来优化nginx的,由nginx_http_core_module(核心模块)组成。在此模块中有几个关键的命令:sendfile、tcp_nopush以及tcp_nodelay。
sendfile
location /css
{
sendfile on;
sendfile_max_chunk 1M;
tcp_nopush on;
}
sendfile on 获取文件跨过用户态。注意,.gzip压缩器需要在用户态进行,因此无法和sendfile共存,如果gizp设置为on,那么sendfile就会失去作用
sendfile_max_chunk 1M 限制最大sendfile的文件大小,防止过大的文件占据整个工作进程
tcp_nopush on 返回数据的首个数据包会携带从sendfile中获取大块的数据后才会被发送,这样可以防止网络拥塞问题。
针对大文件获取
- 对于大文件,不适合进行缓存,如果对大文件进行缓存,那么缓存区就无法存储其他数据,缓存就失去了意义。因此,需要通过Direct I/O 命令对数据不进行缓存。
-
location /media { sendfile off; directio 4k; output_buffers 1 256k; }
- 由于directio会堵塞工作进程,会降低吞吐量,因此可以开启异步的io——
Asynchronous I/O (AIO),如下所示 -
location /media { sendfile off; directio 4k; output_buffers 1 256k; aio on; } - 同时可以把aio和directio 与sendfile 结合,得到如下配置
-
location /media { sendfile on; directio 4k; output_buffers 1 256k; aio on; } - 解读一下,当dirctio大于4k的时候启用异步io,当小于4k的时候 使用sendfile。
sendfile on | off ,指的是这个模块可开可关,默认是开启的。sendfile是加速系统内核传递文件拷贝文件的,当我们拷贝一个文件时,我们的文件是由硬盘到内核空间,再到用户空间,然后再到内核的接口缓存,最后到协议栈。一个文件由硬盘拷贝至操作系统自身,然后操作系统根据令牌(用户id)来分配给root等用户,用户想要发送这个文件则需要再将其拷贝至系统,系统会再将其拷贝至一个网络程序,最后将其发出;一个文件被里里外外拷贝很多次(早期是有意义的),其实这个过程是可以优化的,现在,我们则可以通过开启sendfile的方式来加速系统的运行。sendfile,中文的的字面意思叫做发送文件,开启后,由硬盘拷贝过来的文件可以直接到内核的接口缓存,而无需经过用户空间的内存缓冲区,可以省去一部分的拷贝时间,从而加速了服务器的文件拷贝。在nginx的主配文件中的http中可看到此模块
tcp_nopush:
每一个文件默认都是要被推送出去的,只要是用户产生的信息,正常情况下都会被立刻发送出去。但是,这种工作流程有一个问题,应用程序每产生一次操作就会发送一个包,而通常情况下,一个包会拥有若干字节的数据以及40个字节的包头(ip地址、mac地址、端口号、序列号、数据帧、检验和……),于是就会产生4000%的过载,很轻易地就能使网络发生堵塞,同时也很浪费资源(包括三次握手四次断开,流动滑动窗口、计时器等等)。为了节省资环,我们的数据包可以开动tcp_nopush功能(默认是禁用状态),它可以积累一些数据(通过毫秒级的延迟),使包积累到一定大小后再发送,以此起到优化nginx的作用。
tcp_nodelay:
当nginx程序启动tcp_nopush后,它的数据包(ack确认包)也会延迟推送,计算机就会产生一个问题,即数据包重传(tcp在每次发包时都会开启一个秒表来验证数据包是否到达,到达后对方会回复ack包,若超时则会认为包并未发出)。由于是我们造成了ack的延迟,所以当开启了tcp_nopush后,同时也会启用tcp_nodelay,即ack的包立刻发出,无需等待。但如果不在长连接时,可关闭此模块,因为ack会被立刻发出。
gzip与sendfile的冲突问题
如果Nginx配置文件中同时开启了sendfile on; 和gzip on;,可能会遇到sendfile指令不起作用的问题。这是因为从Nginx 1.1.7版本开始,默认情况下,Nginx的gzip模块会自动使用"sendfile()"函数来发送压缩后的数据,这可能会与sendfile指令冲突。
我们知道,开启sendfile文件高效传输模式,文件在内核空间直接传输给socket,不会经过用户程序,开启gzip之后,该模式也就失效了。那么怎么解决这个问题呢?
在Nginx中,sendfile on; 指令用于启用高效文件传输模式,而gzip on; 则用于启用Gzip压缩以减少传输数据的大小。然而,在某些情况下,两者可能会发生冲突,导致Nginx无法正常工作。
解决方法:
如果你正在使用的是Nginx 1.1.7或更高版本,请确保在gzip配置中设置sendfile_max_chunk指令,并为其指定一个适当的值。例如:
gzip_sendfile_max_chunk 512k;
这将限制gzip压缩的每个数据块的大小,以避免影响sendfile的性能。
如果你正在使用的是Nginx 1.1.7之前的版本,则不存在这个问题。
另一种解决方法是关闭Nginx的sendfile自动用于gzip,通过设置gzip_disable_sendfile on;。这样可以确保Nginx不会自动使用gzip压缩的数据发送,而是使用标准的sendfile方法。
示例配置:
gzip on;
gzip_disable_sendfile on; # 关闭自动使用sendfile的gzip
sendfile on;
2、可以通过引入ngx_http_gzip_static_module模块来进行解决。该模块允许发送文件扩展名为.gz的预压缩文件。
ngx_http_gzip_module模块
ngx_http_gzip_static_module模块
ngx_http_gunzip_module模块
注意:本文归作者所有,未经作者允许,不得转载
