Nginx 切片模块、断点续传
熟悉 CDN 行业主流技术的朋友应该都比较清楚,虽然 Nginx 近几年发展的如日中天,但是基本上没有直接使用它自带的 proxy_cache 模块来做缓存的,原因有很多,例如下面几个:
不支持多盘
不支持裸设备
大文件不会切片
大文件的 Range 请求表现不尽如人意
Nginx 自身不支持合并回源
在现在主流的 CDN 技术栈里面, Nginx 起到的多是一个粘合剂的作用,例如调度器、负载均衡器、业务逻辑(防盗链等),需要与 Squid、ATS 等主流 Cache Server 配合使用,
Nginx-1.9.8 中新增加的一个模块ngx_http_slice_module解决了一部分问题。
首先,我们看看几个不同版本的 Nginx 的 proxy_cache 对 Range 的处理情况。
Nginx-0.8.15
在 Nginx-0.8.15 中,使用如下配置文件做测试:
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
proxy_cache_path /tmp/nginx/cache levels=1:2 keys_zone=cache:100m;
server {
listen 8087;
server_name localhost;
location / {
proxy_cache cache;
proxy_cache_valid 200 206 1h;
# proxy_set_header Range $http_range;
proxy_pass http://127.0.0.1:8080;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
重点说明以下两种情况:
第一次 Range 请求(没有本地缓存),Nginx 会去后端将整个文件拉取下来(后端响应码是200)后,并且返回给客户端的是整个文件,响应状态码是200,而非206. 后续的 Range 请求则都用缓存下来的本地文件提供服务,且响应状态码都是对应的206了。
如果在上面的配置文件中,加上 proxy_set_header Range $http_range;再进行测试(测试前先清空 Nginx 本地缓存)。则第一次 Range 请求(没有本地缓存),Nginx 会去后端用 Range 请求文件,而不会把整个文件拉下来,响应给客户端的也是206.但问题在于,由于没有把 Range 请求加入到 cache key 中,会导致后续所有的请求,不管 Range 如何,只要 url 不变,都会直接用cache 的内容来返回给客户端,这肯定是不符合要求的。
Nginx-1.9.7
在 Nginx-1.9.7 中,同样进行上面两种情况的测试,第二种情况的结果其实是没多少意义,而且肯定也和 Nginx-0.8.15 一样,所以这里只关注第一种测试情况。
第一次 Range 请求(没有本地缓存),Nginx 会去后端将整个文件拉取下来(后端响应码是200),但返回给客户端的是正确的 Range 响应,即206.后续的 Range 请求,则都用缓存下来的本地文件提供服务,且都是正常的206响应。
可见,与之前的版本相比,还是有改进的,但并没有解决最实质的问题。
我们可以看看 Nginx 官方对于 Cache 在 Range 请求时行为的说明:
How Does NGINX Handle Byte Range Requests?
If the file is up-to-date in the cache, then NGINX honors a byte range request and serves only the specified bytes of the item to the client. If the file is not cached, or if it’s stale, NGINX downloads the entire file from the origin server. If the request is for a single byte range, NGINX sends that range to the client as soon as it is encountered in the download stream. If the request specifies multiple byte ranges within the same file, NGINX delivers the entire file to the client when the download completes.
Once the download completes, NGINX moves the entire resource into the cache so that all future byte-range requests, whether for a single range or multiple ranges, are satisfied immediately from the cache.
Nginx-1.9.8
我们继续看看Nginx-1.9.8, 当然,在编译时要加上参数--with-http_slice_module,并作类似下面的配置:
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
proxy_cache_path /tmp/nginx/cache levels=1:2 keys_zone=cache:100m;
server {
listen 8087;
server_name localhost;
location / {
slice 1m;
proxy_cache cache;
proxy_cache_key $uri$is_args$args$slice_range;
proxy_set_header Range $slice_range;
proxy_cache_valid 200 206 1h;
#proxy_set_header Range $http_range;
proxy_pass http://127.0.0.1:8080;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
不测不知道,一侧吓一跳,这俨然是一个杀手级的特性。
首先,如果不带 Range 请求,后端大文件在本地 cache 时,会按照配置的 slice 大小进行切片存储。
其次,如果带 Range 请求,则 Nginx 会用合适的 Range 大小(以 slice 为边界)去后端请求,这个大小跟客户端请求的 Range 可能不一样,并将以 slice 为大小的切片存储到本地,并以正确的206响应客户端。
注意上面所说的,Nginx 到后端的 Range 并不一定等于客户端请求的 Range,因为无论你请求的Range 如何,Nginx 到后端总是以 slice 大小为边界,将客户端请求分割成若干个子请求到后端,假设配置的 slice 大小为1M,即1024字节,那么如果客户端请求 Range 为0-1023范围以内任何数字,均会落到第一个切片上,如果请求的 Range 横跨了几个 slice 大小,则nginx会向后端发起多个子请求,将这几个 slice 缓存下来。而对客户端,均以客户端请求的 Range 为准。如果一个请求中,有一部分文件之前没有缓存下来,则 Nginx 只会去向后端请求缺失的那些切片。
由于这个模块是建立在子请求的基础上,会有这么一个潜在问题:当文件很大或者 slice 很小的时候,会按照 slice 大小分成很多个子请求,而这些个子请求并不会马上释放自己的资源,可能会导致文件描述符耗尽等情况。
小结
总结一下,需要注意的点:
该模块用在 proxy_cache 大文件的场景,将大文件切片缓存
编译时对 configure 加上 --with-http_slice_module 参数
$slice_range 一定要加到 proxy_cache_key 中,并使用 proxy_set_header 将其作为 Range 头传递给后端
要根据文件大小合理设置 slice 大小
具体特性的说明,可以参考 Roman Arutyunyan 提出这个 patch 时的邮件来往:
https://forum.nginx.org/read.php?29,261929,261929#msg-261929
顺带提一下,Roman Arutyunyan 也是个大牛,做流媒体领域的同学们肯定很多都听说过:nginx-rtmp 模块的作者。
参考资料
http://www.tianwaihome.com/2015/03/nginx-proxy-cache.html
- Nginx 官方的 Cache 指南
https://www.nginx.com/blog/nginx-caching-guide/
- Nginx各版本changelog
- Nginx proxy 模块 wiki
http://nginx.org/en/docs/http/ngx_http_proxy_module.html
- http_slice_module 的历次提交记录
http://hg.nginx.org/nginx/rev/29f35e60840b
http://hg.nginx.org/nginx/rev/bc9ea464e354
http://hg.nginx.org/nginx/rev/4f0f4f02c98f
- http_slice_module 提交前的邮件来往
https://forum.nginx.org/read.php?29,261929
- Nginx 之前版本关于 Range cache 的邮件来往
https://forum.nginx.org/read.php?2,8958,8958
- 切片模块的 wiki
http://nginx.org/en/docs/http/ngx_http_slice_module.html
本文来源于:http://www.pureage.info/2015/12/10/nginx-slice-module.html
Nginx 切片模块、断点续传的更多相关文章
- 基于Nginx dyups模块的站点动态上下线并实现简单服务治理
简介 今天主要讨论一下,对于分布式服务,站点如何平滑的上下线问题. 分布式服务 在分布式服务下,我们会用nginx做负载均衡, 业务站点访问某服务站点的时候, 统一走nginx, 然后nginx根据一 ...
- nginx事件模块分析(一)
nginx ngx_events_module模块分析 ngx_events_module模块是核心模块之一,它是其它所有事件模块的代理模块.nginx在启动时只与events模块打交道,而由even ...
- mac下Nginx+lua模块编译安装
Nginx的nb之处就不说了,lua也是一个小巧的脚本语言,由标准C编写而成,几乎可以运行在所有的平台上,也非常强大,其他特性请自行度娘.nginx_lua_module是由淘宝的工程师清无(王晓哲) ...
- nginx添加模块 (非覆盖安装)
nginx添加模块(非覆盖安装) 原已经安装好的nginx,现在需要添加一个未被编译安装的模块: 查看原来编译时都带了哪些参数# /usr/local/nginx/sbin/nginx -V ngin ...
- 一些好用的nginx第三方模块
一些好用的nginx第三方模块 转自;http://macken.iteye.com/blog/1963301 1.Development Kit https://github.com/simpl/ ...
- nginx自定义模块编写-实时统计模块--转载
原文:http://www.vimer.cn/2012/05/nginx%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A8%A1%E5%9D%97%E7%BC%96%E5%86%99- ...
- nginx -- handler模块(100%)
handler模块简介 相信大家在看了前一章的模块概述以后,都对nginx的模块有了一个基本的认识.基本上作为第三方开发者最可能开发的就是三种类型的模块,即handler,filter和load-ba ...
- Nginx Http模块开发
关于Nginx Http模块开发的文章非常少,只有Emiler的那篇关于Http模块的文章,但是那篇文章里面,并没有说到事件型的模块如何进行开发.而且文章里面提到的内容实在是让人有点意犹未尽.因此,对 ...
- nginx自定义模块编写-根据post参数路由到不同服务器
nginx可以轻松实现根据不同的url 或者 get参数来转发到不同的服务器,然而当我们需要根据http包体来进行请求路由时,nginx默认的配置规则就捉襟见肘了,但是没关系,nginx提供了强大的自 ...
随机推荐
- 第 30 章 使用 Emmet 插件
学习要点: 1.安装方式 2.自定义!生成 3.快速生成 主讲教师:李炎恢 本章主要探讨了解一下 Sublime Text3 的一个 HTML5 代码提示插件:Emmet,这个插件比自带原生的要强 ...
- No.016:3Sum Closest
问题: Given an array S of n integers, find three integers in S such that the sum is closest to a given ...
- spring入门(二)【加载properties文件】
在开发过程当中需要用到配置信息,这些信息不能进行硬编码,这时配置文件是一个比较好的方式,java提供了properties格式的文件,以键值对的方式保存信息,在读取的时候通过键获得键对应的值,spri ...
- ENVI软件操作【数据显示操作——Overlay菜单操作】
一.注记层(Annotation) 注记层是ENVI的一个数据类型,它的后缀名是.ann.往往作为栅格数据层,矢量数据层.三维场景会绘图图表的附加数据叠加在上面,还可以作为镶嵌图像时候的裁剪线.注记数 ...
- [DeviceOne开发]-轮播图和多模板的简单示例
一.简介 这个例子是利用Slideview组件实现循环轮播的效果,同时这个slideview作为一个listview的最上面的一行数, 1. listview有2个模板,一个是以slideview为核 ...
- svn服务端和eclipse配合使用
今天弄了个svn服务器来做项目的版本控制,讲讲我做的步骤吧 1.安装svn服务端 2.下载subclipse插件 3.将subclipse插件安装到eclipse上,点击Install New Sof ...
- 轻松玩转jquery。
一.简介 jQuery创始人是美国John Resig,是优秀的Javascript框架: jQuery是一个轻量级.快速简洁的javaScript库.源码戳这 jQuery产生的对象时jQuery独 ...
- 2013 Visual Studio Magazine读者选择奖界面框架类获奖情况
2013 Visual Studio Magazine读者选择奖已经正式揭晓了!据了解,截至今年此奖项已经评选了21次,非常值得.NET开发人员信赖和参考.此次评选共有400多个产品角逐28个分类的奖 ...
- SharePoint2013 此产品的试用期已结束
今天使用SharePoint 2013创建页面的时候,突然提示“此产品的试用期已结束 ”. 网上解决办法: “将IIS的‘应用程序池’下网站集对应的‘宿主应用程序’的‘应用程序池标识’改为‘域管理员或 ...
- Iphone 英语语言下通讯录排序问题
Iphone 如果把界面语言设置成English,那么通讯录默认排序是通过拼音来排的,如果联系人信息中没有设置名字的拼音,那么这些联系人都会被放到#中. 批量添加拼音的解决方案: https://gi ...