理解Nginx的proxy_buffer缓冲
2018-11-09 17:15:45 阿炯

proxy_buffer 是用于 proxy 模式的缓冲功能。在使用反向代理时,每个客户端将使用两个连接:一个用于响应客户端的请求,另一个用于到后端的访问。当代理到另一台服务器,两个不同的连接速度会影响客户的体验:
从客户机到Nginx代理的连接。
从Nginx代理到后端服务器的连接。


Nginx具有优化这些连接调整其行为的能力。如果没有缓冲,数据从代理的服务器发送并立即开始被发送到客户。如果假定客户端很快,缓冲可以关闭而尽快使数据到客户端,有了缓冲,Nginx代理将暂时存储后端的响应,然后按需供给数据给客户端;如果客户端是缓慢的,允许Nginx在接收到完成到服务器的数据后关闭到后端的连接,然后以任何可能的速度处理到客户端的数据。Nginx默认有缓冲设计,因为客户端往往有很大的不同的连接速度。我们可以用以下指令调节缓冲行为,在HTTP,server或location位置来设置。重要的是要记住,大小size指令是针对每个请求配置的,所以增加超出你需求会影响你的性能。

buffer,即缓冲区,它在 Nginx 上发挥的作用就是启用一个缓冲区,先在这个缓冲区内进行存储,再把数据发送出去。和在线观看视频有点类似,先把视频文件缓冲一部分到本地再开始播放。若没有 buffer,数据将会直接从 Nginx 传输到客户端。假设如果客户端的加载速度足够快,你可以直接把 buffer 关掉,让数据尽可能快地到达客户端。

proxy_buffer的作用

如果没有buffer,数据将直接从 nginx 传输到客户端,假设客户端的加载速度足够快,那么是可以把proxy buffer给关掉的,让数据尽快到达客户端。

若使用proxy buffer,nginx将会临时存储后端response到缓冲区,然后慢慢把数据发送到客户端,启用buffer 的好处在于可以把数据一次性的发送给目标,相较于即时传输可以节约出部分带宽。以上这些参数都是针对每一个http请求的,不是全局设置,比如proxy_buffer_size为4k,代表每一个请求都是4k,而不是所有请求共用4k。proxy buffer不是global而是per request的。

proxy_max_temp_file_size指定当响应内容大于proxy_buffers指定的缓冲区时,写入硬盘的临时文件的大小。如果超过了这个值,nginx将与Proxy服务器同步的传递内容,而不再缓冲到硬盘。设置为0时,则直接关闭硬盘缓冲。

在proxy_buffering开启的情况下,nginx将会尽可能的读取所有的upstream端传输的数据到buffer,直到proxy_buffers设置的所有buffers被写满或者数据被读取完(EOF)。此时nginx开始向客户端传输数据,会同时传输这一整串buffers。同时如果后端响应的内容很大的话,nginx会接收并把他们写入到temp_file里去,大小由 proxy_max_temp_file_size控制。如果busy的buffer传输完了会从temp_file里面接着读数据,直到传输完毕。

一旦proxy_buffers设置的buffer被写入,直到buffer里面的数据被完整的传输完(传输到客户端),这个buffer将会一直处在busy状态,我们不能对这个buffer进行任何别的操作。所有处在busy状态的buffer size加起来不能超过proxy_busy_buffers_size,所以proxy_busy_buffers_size是用来控制同时传输到客户端的buffer数量的。

proxy_temp_file_write_size  64k; #设定缓存文件夹大小,大于这个值(64k),将从upstream服务器传送。

使用 buffer 时,nginx 将会临时存储后端 response 到缓冲区,然后慢慢把数据发送到客户端。启用 buffer 的好处在于可以把数据一次性地发送给目标,相较于即时传输可以节约出这部分带宽。nginx 全局配置中的 tcp_nopush 的作用就是数据包会累计到一定大小之后才会发送。而 tcp_nodelay 是尽快发送数据,所以若你启用了 buffer,建议关闭 tcp_nodelay。

小结下 nginx buffer 设置:
proxy_buffer_size 4k;#设置代理服务器(nginx)保存用户头信息的缓冲区大小
proxy_buffering on;
proxy_buffers 4 32k;#缓冲区,网页平均在32k以下的设置
proxy_busy_buffers_size 64k;#高负荷下缓冲大小(proxy_buffers*2)
proxy_temp_file_write_size 64k;#设定缓存文件夹大小,大于此值将从upstream服务器传
proxy_max_temp_file_size 1024m;

首先,这些参数都是针对每一个http request,不是全局的。

如果没有缓冲,数据从代理的服务器发送并立即开始被发送到客户。如果假定客户端很快,缓冲可以关闭而尽快使数据到客户端,有了缓冲,Nginx 代理将暂时存储后端的响应,然后按需供给数据给客户端。如果客户端是缓慢的,允许Nginx服务器关闭到后端的连接。然后它可以处理数据分配到客户端,以任何可能的速度。

Nginx默认有缓冲设计,因为客户端往往有很大的不同的连接速度。我们可以用以下指令调节缓冲行为,可在HTTP,server或 location位置来设置。重要的是要记住,大小size指令是针对每个请求配置的,所以增加超出你需求会影响你的性能,如果这时有许多客户端请求:

proxy_buffering:该指令控制缓冲是否启用。默认情况下,它的值是'on'。

proxy_buffers:该指令控制代理响应缓冲区的数量(第一个参数)和大小(第二个参数)。默认配置是8个缓冲区大小等于一个内存页(4K或者8K)。增加缓冲区的数目可以让你缓冲更多信息。

proxy_buffer_size:从后端服务器的响应头缓冲区大小,它包含headers,和其他部分响应是分开的。该指令设置响应部分的缓冲区大小。默认情况下,它和proxy_buffers是相同的尺寸,但因为这是用于头信息,这通常可以设置为一个较低的值。

proxy_busy_buffers_size:此指令设置标注“client-ready”缓冲区的最大尺寸。而客户端可以一次读取来自一个缓冲区的数据,缓冲被放置在队列中,批量发送到客户端。此指令控制允许是在这种状态下的缓冲空间的大小。

proxy_max_temp_file_size:这是每个请求能用磁盘上临时文件最大大小。这些当上游响应太大不能装配到缓冲区时被创建。

proxy_temp_file_write_size:这是当被代理服务器的响应过大时Nginx一次性写入临时文件的数据量。

proxy_temp_path:当上游服务器的响应过大不能存储到配置的缓冲区域时,Nginx存储临时文件硬盘路径。


Nginx提供了相当多的不同的指令来调整缓冲行为。大多数时候,不必担心太多,但它对于调整一些值可能是有用的。可能最有用的调整是proxy_buffers和proxy_buffer_size指令。

proxy_buffer 的配置

proxy_buffer 是用于 proxy 模式(一般也可称为反向代理)的 buffer 配置。Nginx 有另外一种适用于 server 模式的 buffer 配置。

proxy_buffer 包括了以下配置项:

需注意,以下指定的数值都是针对每一个 http request 的上限,而不是对于整个 buffer 区指定的上限。

proxy_buffering

proxy_buffering on;

在 proxy_buffering 开启的时候,proxy_buffers 和 proxy_busy_buffers_size 才会起作用。

proxy_buffers

proxy_buffers 4 8k;

指定一个 request 的 buffer 的数量和大小。

proxy_buffer_size


proxy_buffer_size 4k;

指定后端 response 的 buffer 的大小。它是来自后端 response 的一部分,它包含 Headers,从 response 分离出来。它仅用于限定 headers 的 buffer 区,所以它的值比 proxy_buffers 更低。

proxy_buffer_size 有一点特殊在于,无论 proxy_buffering 是否开启,proxy_buffer_size 都会起作用。

proxy_busy_buffers_size

proxy_busy_buffers_size 12k;

忙时 buffer 的最大值。一个客户端一次只能从一个 buffer 中读取数据的同时,剩下的 buffer 会被放到队列中,等待发送到客户端,这个 directive 指定在这个状态下的 buffer 的大小。

如果 proxy_buffers 关闭
Nginx不会尝试获取到后端服务器所有响应数据之后才返回给客户端,Nginx 会尽快把数据传给客户端,在数据传完之前,Nginx 接收到的最大缓存大小不能超过 proxy_buffer_size 。

如果 proxy_buffers 打开
Nginx将会尽可能的读取后端服务器的数据到buffer,直到proxy_buffers设置的所有buffer们被写满或者数据被读取完(EOF),此时Nginx开始向客户端传输数据,会同时传输这一整串buffer们。如果数据很大的话,Nginx会接收并把他们写入到temp_file里去,大小由proxy_max_temp_file_size 控制。「当数据没有完全读完的时候」,Nginx同时向客户端传送的buffer大小不能超过 proxy_busy_buffers_size。

# One worker per CPU-core.
worker_processes  2;
events {
    worker_connections  8096;
    multi_accept        on;
    use                 epoll;
}
worker_rlimit_nofile 40000;
http {
    sendfile           on;
    tcp_nopush         on;
    tcp_nodelay        on;
    keepalive_timeout  15;
}

http {
     # Basic reverse proxy server
     upstream backend  {
        server 127.0.0.1:4433;
     }
     # *:80 -> 127.0.0.1:4433
     server {
        listen       80;
        server_name  example.com;
        ## send all traffic to the back-end
        location / {
            proxy_pass        http://backend;
            proxy_redirect    off;
            proxy_set_header  X-Forwarded-For $remote_addr;
        }
     }
}

缓冲控制

如果禁止缓冲,那么当Nginx一收到后端的反馈就同时传给客户端。nginx 不会从被代理的服务器读取整个反馈信息,nginx可从服务器一次接收的最大数据大小由 proxy_buffer_size 控制。

相关参数

proxy_buffer_size
语法: proxy_buffer_size the_size
默认值: proxy_buffer_size 4k/8k
上下文: http, server, location


该指令设置缓冲区大小,从代理后端服务器取得的第一部分的响应内容,会放到这里。小的响应header通常位于这部分响应内容里边。默认来说,该缓冲区大小等于指令proxy_buffers所设置的,但是你可以把它设置得更小。

proxy_buffering
语法: proxy_buffering on|off
默认值: proxy_buffering on
上下文: http, server, location


这个参数用来控制是否打开后端响应内容的缓冲区,如果这个设置为off,那么proxy_buffers和proxy_busy_buffers_size这两个指令将会失效。 但是无论proxy_buffering是否开启,对proxy_buffer_size都是生效的。

proxy_buffering开启的情况下,nignx会把后端返回的内容先放到缓冲区当中,然后再返回给客户端(边收边传,不是全部接收完再传给客户端)。临时文件由proxy_max_temp_file_size和proxy_temp_file_write_size这两个指令决定的。如果响应内容无法放在内存里边,那么部分内容会被写到磁盘上。

如果proxy_buffering关闭,那么nginx会立即把从后端收到的响应内容传送给客户端,每次取的大小为proxy_buffer_size的大小,这样效率肯定会比较低。

nginx不尝试计算被代理服务器整个响应内容的大小,nginx能从服务器接受的最大数据是由指令proxy_buffer_size指定的。

注意:
1、proxy_buffering启用时,要提防使用的代理缓冲区太大。这可能会吃掉你的内存,限制代理能够支持的最大并发连接数。

2、对于基于长轮询(long-polling)的Comet 应用来说,关闭 proxy_buffering 是重要的,不然异步响应将被缓存导致Comet无法工作。

proxy_buffers
语法: proxy_buffers the_number is_size;
默认值: proxy_buffers 8 4k/8k;
上下文: http, server, location


该指令设置缓冲区的大小和数量,从被代理的后端服务器取得的响应内容会放置到这里。默认情况下,一个缓冲区的大小等于内存页面大小,可能是4K也可能是8K,这取决于平台。

proxy_buffers由缓冲区数量和缓冲区大小组成的,总的大小为number*size。

若某些请求的响应过大,则超过_buffers的部分将被缓冲到硬盘(缓冲目录由_temp_path指令指定), 当然这将会使读取响应的速度减慢,影响用户体验。可以使用proxy_max_temp_file_size指令关闭磁盘缓冲。

proxy_busy_buffers_size
语法: proxy_busy_buffers_size size;
默认值: proxy_busy_buffers_size proxy_buffer_size * 2;
上下文: http, server, location, if


proxy_busy_buffers_size不是独立的空间,他是proxy_buffers和proxy_buffer_size的一部分。nginx会在没有完全读完后端响应的时候就开始向客户端传送数据,所以它会划出一部分缓冲区来专门向客户端传送数据(这部分的大小是由proxy_busy_buffers_size来控制的,建议为proxy_buffers中单个缓冲区大小的2倍),然后它继续从后端取数据,缓冲区满了之后就写到磁盘的临时文件中。

proxy_max_temp_file_size和proxy_temp_file_write_size

临时文件由proxy_max_temp_file_size和proxy_temp_file_write_size这两个指令决定。proxy_temp_file_write_size是一次访问能写入的临时文件的大小,默认是proxy_buffer_size和proxy_buffers中设置的缓冲区大小的2倍,Linux下一般是8k。

proxy_max_temp_file_size指定当响应内容大于proxy_buffers指定的缓冲区时,写入硬盘的临时文件的大小。如果超过了这个值,Nginx将与Proxy服务器同步的传递内容,而不再缓冲到硬盘。设置为0时,则直接关闭硬盘缓冲。

buffer工作原理

首先第一个概念是所有的这些proxy buffer参数是作用到每一个请求的。每一个请求会安按照参数的配置获得自己的buffer。proxy buffer不是global而是per request的。

proxy_buffering 是为了开启response buffering of the proxied server,开启后proxy_buffers和proxy_busy_buffers_size参数才会起作用。

无论proxy_buffering是否开启,proxy_buffer_size(main buffer)都是工作的,proxy_buffer_size所设置的buffer_size的作用是用来存储upstream端response的header。

在proxy_buffering 开启的情况下,Nginx将会尽可能的读取所有的upstream端传输的数据到buffer,直到proxy_buffers设置的所有buffer们被写满或者数据被读取完(EOF)。此时nginx开始向客户端传输数据,会同时传输这一整串buffer们。同时如果response的内容很大的话,Nginx会接收并把他们写入到temp_file里去。大小由proxy_max_temp_file_size控制。如果busy的buffer传输完了会从temp_file里面接着读数据,直到传输完毕。

一旦proxy_buffers设置的buffer被写入,直到buffer里面的数据被完整的传输完(传输到客户端),这个buffer将会一直处在busy状态,我们不能对这个buffer进行任何别的操作。所有处在busy状态的buffer size加起来不能超过proxy_busy_buffers_size,所以proxy_busy_buffers_size是用来控制同时传输到客户端的buffer数量的。

缓存和过期控制

上面的配置是将所有请求都转发给后端应用。为避免静态请求给后端应用带来的过大负载,我们可以将nginx配置为缓存那些不变的响应数据,这就意味着nginx不会向后端转发那些请求。下面示例中将 *.html, *.gif, 等文件缓存半小时。

http {
     proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:60m max_size=1G;
}
## send all traffic to the back-end
location / {
    proxy_pass  http://backend;
    proxy_redirect off;
    proxy_set_header        X-Forwarded-For $remote_addr;
    location ~* \.(html|css|jpg|gif|ico|js)$ {
        proxy_cache          cache;
        proxy_cache_key      $host$uri$is_args$args;
        proxy_cache_valid    200 301 302 30m;
        expires              30m;
        proxy_pass  http://backend;
    }
}

将请求缓存到 /tmp/cache,并定义了其大小限制为1G。同时只允许缓存有效的返回数据,例如:
proxy_cache_valid  200 301 302 30m;

所有响应信息的返回代码不是 "HTTP (200|301|302) OK" 的都不会被缓存。

Nginx反向代理传输机制的总结

1)、proxy_buffering这个参数用来控制是否打开后端响应内容的缓冲区,如果这个设置为off,那么proxy_buffers和proxy_busy_buffers_size这两个指令将会失效。

但是无论proxy_buffering是否开启,对proxy_buffer_size都是生效的。

2)、proxy_buffering开启的情况下,nignx会把后端返回的内容先放到缓冲区当中,然后再返回给客户端(边收边传,不是全部接收完再传给客户端)。

临时文件由proxy_max_temp_file_size和proxy_temp_file_write_size这两个指令决定的。如果proxy_buffering关闭,那么nginx会立即把从后端收到的响应内容传送给客户端,每次取的大小为proxy_buffer_size的大小,这样效率肯定会比较低。

3)、后端服务器的相应头会放到proxy_buffer_size当中,这个大小默认等于proxy_buffers当中的设置单个缓冲区的大小。

而proxy_buffers当中单个缓冲区的大小是由系统的内存页面大小决定的,Linux系统中一般为4k。查看方法:
# getconf PAGE_SIZE
4096

虽然默认是相等的,但是proxy_buffers的缓冲区大小一般会设置的比较大,以应付较大的网页。而proxy_buffer_size只是响应头的缓冲区,没有必要也跟着加到那么大。所以proxy_buffer_size最好单独设置,一般设置个4k就够了。

4)、proxy_buffers是有缓冲区数量和缓冲区大小组成的,总的大小为number*size。

proxy_busy_buffers_size不是独立的空间,他是proxy_buffers和proxy_buffer_size的一部分。nginx会在没有完全读完后端响应的时候就开始向客户端传送数据,所以它会划出一部分缓冲区来专门想客户端传送数据(这部分的大小是由proxy_busy_buffers_size来控制的,建议为proxy_buffers中单个缓冲区大小的2倍),然后它继续从后端取数据,缓冲区满了之后就写到磁盘的临时文件中。

5)、临时文件由proxy_max_temp_file_size和proxy_temp_file_write_size这两个指令决定。

proxy_max_temp_file_size是临时文件的总大小,默认1024m。客户端下载大文件时候,由后端程序返回给nginx时且文件有1G以上的时候总是无法正确下载;在nginx.conf里给proxy_max_temp_file_size 改成0就没限制了,如果需要限制,就调大一点,比如5120m。

proxy_temp_file_write_size是一次访问能写入的临时文件的大小,默认是proxy_buffer_size和proxy_buffers中设置的缓冲区大小的2倍,Linux下一般是8k。

关于缓存的注意事项

高速缓存能够极大地提高代理服务器的性能,不过也要明确的考虑配置缓存时候。

首先,任何用户相关的数据不应被高速缓存,这可能导致一个用户的数据被呈现给其他用户。如果你的网站是完全静态的,这可能不是一个问题。如果网站有一些动态元素,将不得不考虑到这一点,如何处理要看是什么应用程序或服务器处理的后端处理。对于私人的内容,你应该设置Cache-Control头为“no-cache”,“no-sotre”,或者"private"依赖于数据的性质:

no-cache:

请求:告知缓存者,必须原原本本的转发原始请求,并告知任何缓存者,需要去转发请求,并验证缓存(如果有的话).对应名词:端对端重载。

响应:允许缓存者缓存副本.那么其实际价值是,总是强制缓存者,校验缓存的新鲜度.一旦确认新鲜,则可以使用缓存副本作为响应。no-cache,还可以指定某个包含字段,比如一个典型应用,no-cache=Set-Cookie.这样做的结果就是告知缓存者,对于Set-Cookie字段,你不要使用缓存内容,而是使用最新的。其他内容则可以使用缓存。

no-store:表示在任何时候收到的数据不被缓存。这对于私人数据是最安全,因为它意味着,该数据必须从服务器每次进行检索。

private:这表明共享的缓存空间不能缓存此数据。这可以用于指示用户的浏览器高速缓存数据,但代理服务器不应当考虑随后的请求数据有效。

public:这表明该响应是可在连接的任何点被高速缓存的公共数据。

一个相关的可以控制此行为报头是max-age头,其指示任何资源应该缓存的秒数。

根据内容的敏感性,正确设置这些头,会帮助你利用缓存优势,同时保持你的私人数据安全,并使您的动态数据最新。如果你的后端也使用Nginx,你可以设置使用过期指令,设置max-age来实现Cache-Control:

location / {
 expires 60m;
}

location /check-me {
 expires -1;
}

在上面的例子中,第一个块允许缓存一个小时的内容,第二块设置Cache-Control头为“无缓存”。要设置其他值,可以使用add_header指令,就像这样:
location /private {
 expires -1;
 add_header Cache-Control "no-store";
}


当header过大时,在日志中会有报'upstream sent too big header while reading response header from upstream',可以尝试加大这三个参数设置:
proxy_buffer_size 64k;
proxy_buffers   4 32k;
proxy_busy_buffers_size 64k;


Nginx做反向代理时响应状态码206问题

在访问前端Nginx服务器的时候发现一些比较大的js和图片加载不完整,会报出206(Partial Content)错误(部分内容):服务器成功处理了部分 GET 请求。直接访问相关js也发现内容确实只加载了一部分,刷新浏览器,发现js内容会增多或图片会多显示出一部分来。当刷新多次之后发现才能全部加载出来,在清空缓存后,还是206错误,又要刷新很多次才能将资源加载完。

问题原因

Nginx代理之后会有相应的代理缓存区,缓存区默认只有几十K,一些版本的nginx默认设置中没有相关处理,导致部分代理文件是会出现加载不全的现象,其实不仅仅是JS或图片文件,只要是请求的文件略大,都会经常出现类似问题。

Nginx在进行反向代理的时候,遇到超出文件大小 proxy_buffer_size 的时候,是一次性把文件都加载到临时目录,然后再发送给用户。如果设置了 proxy_buffering off 则不会加载到临时目录,而是同步的从上游进行加载。可以通过设置 proxy_max_temp_file_size 参数来设置最大可以缓存的文件大小来缓解此问题。

问题解决

在nginx.conf中的http段中添加:
proxy_buffer_size 128k;
proxy_buffers   32 128k;
proxy_busy_buffers_size 128k;

206 和 Byte Range 的问题

Byte Range允许客户端向服务器请求一部分文件,而不是整个文件。大部分支持多线程下载和断点下载的软件都是用的这个功能。这个时候服务器返回的Http Code是206 Partial Requests。

但是Nginx做反代的时候,如果没有好好的设置,这个功能可能会引来Dos攻击。因为默认做反向代理的时候,Nginx向后端服务器请求的时候是不会把 Range 参数加上的,而是会去请求整个文件,比方说有一个1G的文件,每次请求1M,Nginx会在每次请求的时候去后端请求一个完整的1G文件,然后取出其中的1M发给客户端,这个时候中间的流量会暴增,导致整个服务器宕机。

解决方案也很简单,把 Range 加到Header里就行了:
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_no_cache $http_range $http_if_range;

查证远程服务器是否支持HTTP 206

首先需要知道文件大小以及远程服务器是否支持HTTP 206请求,使用curl命令可以查看任意资源的HTTP头,使用下面的curl命令可以发送一个HEAD请求:
$ curl -I http://www.freeoa.net/images/notexist.png

输出结果为:
HTTP/1.1 200 OK
Content-Type: image/png
Content-Length: 36907
Connection: keep-alive
Server: nginx
Date: Wed, 07 Nov 2012 00:44:47 GMT
Cache-Control: public, max-age=432000000
Expires: Fri, 17 Jul 2026 00:44:46 GMT
Accept-Ranges: bytes
ETag: "278000835"
Last-Modified: Mon, 05 Nov 2012 23:06:34 GMT
Age: 298127

其中有两个我们比较关注的请求头:
Accept-Ranges: bytes - 该响应头表明服务器支持Range请求,以及服务器所支持的单位是字节(这也是唯一可用的单位);还能知道服务器支持断点续传,以及支持同时下载文件的多个部分,也就是说下载工具可以利用范围请求加速下载该文件。如果响应为 Accept-Ranges: none 的响应头表示服务器不支持范围请求。

Content-Length: 36907 -  Content-Length响应头表明了响应实体的大小,也就是真实的图片文件的大小是36907字节 (37K)。

如何发送一个range请求头

该图片所在的服务器支持范围请求,需要发送一个包含Range请求头的GET请求:
Range: bytes=0-1024

完整的请求数据应该是这样的,首先第一行是:
GET /images/notexist.png HTTP/1.1

然后需要发送Host请求头来指定请求资源所在的主机和端口号:
Host: www.freeoa.net

最后是要发送的Range请求头,指定了你想要的字节范围:
Range: bytes=0-1024

使用telnet命令

telnet允许你使用Telnet协议来与远程主机(服务器)进行通信,所有的类Unix操作系统以及MS-Windows都包含有Telnet客户端,启动Telnet客户端并进入Telnet提示符,要执行命令:
telnet your-server-name-here www
telnet your-server-name-here 80

想要通过端口号80连接远程服务器www.freeoa.net,输入:
telnet www.freeoa.net 80

输出结果为:
Trying 1.2.3.4...
Connected to www.freeoa.net.
Escape character is '^]'.

在本例中使用范围请求(0-1024 字节)来请求www.freeoa.net上的/images/notexist.png文件,输入:

GET /images/notexist.png HTTP/1.1
Host: www.freeoa.net
Range: bytes=0-1024

使用curl命令

curl是一个和远程服务器交换数据的工具,它支持HTTP/FTPSFTP/FILE协议上的范围请求,在下例中使用两段范围来请求远程文件notexist.png,然后使用cat命令将两段数据合并成完整文件:
curl  --header "Range: bytes=0-20000" http://www.freeoa.net/images/notexist.png -o part1
curl  --header "Range: bytes=20001-36907" http://www.freeoa.net/images/notexist.png -o part2
cat part1 part2 >> test1.png

还可以使用-r选项(可以同时添加-v选项查看请求头和响应头):
curl  -r 0-20000 http://www.freeoa.net/images/notexist.png -o part1
curl  -r 20001-36907 http://www.freeoa.net/images/notexist.png -o part2
cat part1 part2 >> test2.png

如何开启Accept-Ranges响应头

大部分web服务器都原生支持字节范围请求,Apache 2.x用户可以在httpd.conf中尝试mod_headers:
Header set Accept-Ranges bytes

Lighttpd用户尝试在lighttpd.conf中进行下面的配置:
## enabled for all file types ##
server.range-requests = "enable"
## But, disable it for mkv files ##
$HTTP["url"] =~ "\.mkv$" {
    server.range-requests = "disable"
}

可以利用范围请求来分段下载一个大文件,如果指定的偏移量是有效的,则服务器会返回一个HTTP 206状态码;如果偏移量是无效的,则服务器会返回一个HTTP 416状态码 (请求范围无法满足)。

一例因权限问题导致的多次请求才能完整显示的问题分析

proxy_temp/5/02/0000000025" failed (13: Permission denied) while reading upstream, client: xxx.xxx.xxx.xxx, server: xxx.freeoa.net, request: ....

问题就出在proxy_temp_file_write_size上,当你的文件超过该参数设置的大小时,nginx会先将文件写入临时目录(缺省为nginx安装目下的proxy_temp目录)。

缺省nginx是以nginx身份启动的,查看proxy_temp目录权限时发现nginx用户没有写入的权限,因此会出现上面的问题:间断性传输与日志中报权限拒绝。将此目录权限修正后再重置nginx后解决。

浏览器端报 net::ERR_INCOMPLETE_CHUNKED_ENCODING 错误

这个错误报在浏览器端,而nginx服务器返回的状态码为200。

chrome打开调试模式,failed错误代码(failed) net::ERR_INCOMPLETE_CHUNKED_ENCODING。该问题是当输出代理文件大小超过配置proxy_temp_file_write_size时候,nginx会将文件写入到临时目录下。如果没有相应的权限,chrom就会进到直接failed而不输出东西。

具体错误:
/var/lib/nginx/tmp/fastcgi/2/56/xxx failed (13: Permission denied) while reading upstream, client: 12.34.56.78, server: abc.freeoa.net, request: "GET /someurl HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "z.freeoa.net:8081"

解决方法:
chown -R the_ng_user:the_ng_group /var/lib/nginx

即将目录更改为nginx运行用户及组。

在碰到输出较大的body的时候,就触发了这个net::ERR_INCOMPLETE_CHUNKED_ENCODING错误。除了限制返回数据大小量能解决该问题外,还可以加大Nginx的buffer可以临时解决这个问题。如以下Nginx配置:
http{
  proxy_buffer_size 128k;
  proxy_buffers   32 128k;
  proxy_busy_buffers_size 128k;
}

当代理文件大小超过配置的proxy_temp_file_write_size值时,nginx会将文件写入到临时目录下(默认为proxy_temp)。如果nginx的运行用户对proxy_temp没有写权限,当然就写不进去,因此出现上面的错误。

nginx写入临时文件相关的参数:
proxy_max_temp_file_size proxy_buffer_size proxy_buffers

参考来源:

ngx_http_proxy_module

关于nginx反向代理传输机制的总结

Nginx作为反向代理优化要点proxy_buffering