您当前的位置:首页 > 计算机 > 服务器 > Nginx

Nginx---slice模块,大文件分片请求

时间:11-21来源:作者:点击数:

Nginx的slice模块可以将一个请求分解成多个子请求,每个子请求返回响应内容的一个片段,让大文件的缓存更有效率。。

HTTP Range请求:

HTTP客户端下载文件时,如果发生了网络中断,必须重新向服务器发起HTTP请求,这时客户端已经有了文件的一部分,只需要请求剩余的内容,而不需要传输整个文件,Range请求就可以用来处理这种问题。

如果HTTP请求的头部有Range字段,如下面所示:

表示客户端请求文件的第1025到第2048个字节,这时服务器只会响应文件的这部分内容,响应的状态码为206,表示返回是响应的一部分,如果服务器不支持Range请求,仍然会返回整个文件,这是状态码200。


 实现原理:

客户端请求100个字节,起始于150,请求内容的范围是150-249。发到nginx之后根据slice配置,比如配置为100,那么就是0-100,100-200,200-300,这样就分为了3块,但是最终这个文件有多大就切分为多少块。之后nginx就会构造两个请求,第一个请求时100-199,然后第二个请求时200-300的。这两个请求返回之后会生成两个文件,第一个100-199,200-299。然后将其组合起来生成客户端要的150-249这样一个响应。

过程总结:以客户端向nginx请求一个10M文件,nginx进行4m的切片为例

  1. 客户端向nginx请求10M;
  2. nginx发起第一个切片(主请求)请求range:0-4194303;
  3. 第一个切片(主请求)请求的内容全部发给客户端后,在slice模块的body_filter发起第二个切片请求(子请求),请求range:4194304-8388607;
  4. 第二个切片请求(子请求)的内容完全发完给客户端后,切回主请求;
  5. 主请求在 slice模块的body_filter发起第三个切片请求(子请求),请求range:8388608-12582911;
  6. 第三个请求(子请求)的内容(8388608-10485759)完全发完给客户端后,切回主请求;
  7. 主请求在slice模块的body_filter判断已经将10M的文件发给客户端,不再进行slice模块的处理。

优势:

    每个子请求收到的数据都会形成一个独立文件,这个文件就是通过proxy_cache_key $uri$is_args$args$slice_range;来定位的,也就是说文件跟key有绑定关系。这种将切片缓存为独立文件的方式,还获得了一个额外的优势。就是如果某个片取源连接断掉,那么前面已经缓存的片依然有效。试想一个大文件(在未开启分片的功能下)在最后还有几个字节就收完的时候,连接突然断了,那么前面的内容都会作废,努力全都付诸东流了。这是基于完整文件的缓存模式下,一个很讨厌的问题。

缺点:

    当文件很大或slice很小时,可导致文件描述符耗尽


需要用到nginx的http_slice_module模块,该模块与 1.9.8版本引入。

配置语法:

slice size;

默认:slice 0;

配置块:http、server、location

size 是一个大小,为0的时候表示禁用这个功能,后面跟上一个size表示通过range协议将大文件分解为多个小文件独立的缓存,当客户端发来的请求中已经含有range协议时可以更好的服务。

Nginx配置文件:

比如nginx设置的slice为2M,那么每个分片是2M。

nginx一开始并不知道要发几个range子请求,它会根据配置的slice2m;,先发起一个2m的range请求,这个请求返回的Content-range头会给出文件总长度,这样nginx就知道一共需要发几个range请求来取完所有内容。

假如原始range请求的访问是0.8M-5.3M,即,这个range请求会在nginx内部被转变成r1(0-2M)、r2(2-4M)、r3(4-6M)三子请求 ,顺序分别发送到ats上;

slice_range变量

slice_range这个变量作用非常特殊,这个变量的值是当前需要向源服务器请求的分片,如果分片的大小为1m,那么最开始变量的值为bytes=0-1048575,通过配置文件中的proxy_set_header Range $slice_range;可以知道取源时请求的Range头部为Range:bytes=0-1048575,源服务器如果支持Range请求,便会返回响应的前1m字节,得到这个响应后slice_range变量的值变为bytes=1048576-2097171 ,再次取源时便会取后1m字节,依次直到取得全部响应内容。


示例

/etc/nginx/conf.d/download.conf

server {
    listen       8081;
    server_name 127.0.0.1;
    location / {
        root /usr/share/nginx/html/;
        index index.html;
    }
}

>>>代理服务器不使用slice模块:/etc/nginx/conf.d/slice.conf

​proxy_cache_path /etc/nginx/cache_slice levels=1:2 keys_zone=slice_cache:10m max_size=10g inactive=60m use_temp_path=off;

server {
    listen       8080;
    server_name  localhost;
    
    #charset koi8-r;
    access_log  /var/log/nginx/slice.log  main;

    location / {
        proxy_cache slice_cache;
        # slice 1m;
        # proxy_cache_key $host$uri$is_args$args$slice_range;
        # proxy_set_header Range $slice_range;
           
        proxy_cache_valid 200 304 12h;
        proxy_cache_valid any 10m;
        add_header Nginx-Cache "$upstream_cache_status";  # 增加一个http响应头信息,Nginx-Cache,告诉客户端是否已经命中代理缓存
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        
        proxy_pass http://127.0.0.1:8081;
    }
}

#让客户端使用range协议,-r表示使用range协议会构造range的头部,我只访问 1048565-1048574 即1M里面的10个字节。可以看到返回也确实只有10个字节

访问到上游但是只返回了10个字节,上游究竟发生了什么,192.168.11.135:8081为上游服务器日志

可以看到上游直接返回了2.24M完整的响应(这里我只访问10个字节却给我返回了2.24M),这是nginx做的一个优化,你只是访问了大文件range其中一小部分,但是nginx考虑到一次性向上游取到整个响应内容,后续再访问到其他字节就可以直接使用我的缓存了。

但是如果我们的服务是并发的,同时有多个客户去访问大文件的某一块的话就会引发很严重的问题。很多请求都会去访问一个巨大的文件,这个时候slice模块就有了用武之地。

  

>>>代理服务器使用slice模块:/etc/nginx/conf.d/slice.conf

使用slice该模块需要配置3个地方,slice后面要有一个单位,即分为多大的大小进行切分,如果分的特别小会造成很多文件,如果分的特别大效果就不会特别明显。这里分为1M。

proxy_cache_key   $uri$is_args$args$slice_range ;  $slice_range ,这样才能知道客户端请求的内容是 所少 字节(1M)。

proxy_set_header  Range $slice_range   必须保证我们的 range 这个头部是发到上游的,发送到上游的单位是 1M ,即每次 1M 发往上游。

​proxy_cache_path /etc/nginx/cache_slice levels=1:2 keys_zone=slice_cache:10m max_size=10g inactive=60m use_temp_path=off;

server {
    listen       8080;
    server_name  localhost;
    
    #charset koi8-r;
    access_log  /var/log/nginx/slice.log  main;

    location / {
        proxy_cache slice_cache;
        slice 1m;
        proxy_cache_key $host$uri$is_args$args$slice_range;
        proxy_set_header Range $slice_range;
           
        proxy_cache_valid 200 304 12h;
        proxy_cache_valid any 10m;
        add_header Nginx-Cache "$upstream_cache_status";  # 增加一个http响应头信息,Nginx-Cache,告诉客户端是否已经命中代理缓存
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        
        proxy_pass http://127.0.0.1:8081;
    }
}

上游服务器的日志:

可以看到请求现在变为了1M,而不是2.24M.

当上游返回巨大文件的时候,使用slice可以针对如果客户端使用断点续传,多线程下载等等含有range场景。那么slice模块是非常有用的

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐