熟悉 CDN 行业主流技术的朋友应该都比较清楚,虽然 Nginx 近几年发展的如日中天,可是基本上没有直接使用它自带的 proxy_cache 模块来作缓存的,缘由有不少,例以下面几个:php
不支持多盘html
不支持裸设备nginx
大文件不会切片git
大文件的 Range 请求表现不尽如人意github
Nginx 自身不支持合并回源后端
在如今主流的 CDN 技术栈里面, Nginx 起到的可能是一个粘合剂的做用,例如调度器、负载均衡器、业务逻辑(防盗链等),须要与 Squid、ATS 等主流 Cache Server 配合使用,缓存
Nginx-1.9.8 中新增长的一个模块ngx_http_slice_module解决了一部分问题。bash
首先,咱们看看几个不一样版本的 Nginx 的 proxy_cache 对 Range 的处理状况。app
在 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-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, 固然,在编译时要加上参数--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
https://www.nginx.com/blog/nginx-caching-guide/
http://nginx.org/en/docs/http/ngx_http_proxy_module.html
http://hg.nginx.org/nginx/rev/29f35e60840b
http://hg.nginx.org/nginx/rev/bc9ea464e354
http://hg.nginx.org/nginx/rev/4f0f4f02c98f
https://forum.nginx.org/read.php?29,261929
https://forum.nginx.org/read.php?2,8958,8958
http://nginx.org/en/docs/http/ngx_http_slice_module.html
本文来源于:http://www.pureage.info/2015/12/10/nginx-slice-module.html