Nginx-1.9.8推出的切片模块
熟悉 CDN 行業(yè)主流技術(shù)的朋友應(yīng)該都比較清楚,雖然 Nginx 近幾年發(fā)展的如日中天,但是基本上沒有直接使用它自帶的 proxy_cache 模塊來做緩存的,原因有很多,例如下面幾個(gè):
- 不支持多盤
- 不支持裸設(shè)備
- 大文件不會(huì)切片
- 大文件的 Range 請(qǐng)求表現(xiàn)不盡如人意
- Nginx 自身不支持合并回源
當(dāng)然,上面列出來的并不全, 所以,在現(xiàn)在主流的 CDN 技術(shù)棧里面, Nginx 起到的多是一個(gè)粘合劑的作用,例如調(diào)度器、負(fù)載均衡器、業(yè)務(wù)邏輯(防盜鏈等),需要與 Squid、ATS 等主流 Cache Server 配合使用,即使不使用這些技術(shù),也會(huì)使用其它辦法,例如直接使用文件系統(tǒng)和數(shù)據(jù)庫去管理文件,而不會(huì)直接使用 proxy_cache 模塊。
Nginx-1.9.8 中新增加的一個(gè)模塊ngx_http_slice_module解決了一部分問題。本文就來嘗嘗鮮,看看這個(gè)切片模塊。
注意:截至到發(fā)文時(shí),Nginx 馬上發(fā)布了 Nginx-1.9.9,用來解決 Nginx-1.9.8中的一個(gè) Bug,所以,在實(shí)際使用中,如果需要使用本新增特性,請(qǐng)直接使用 Nginx-1.9.9。
首先,我們看看幾個(gè)不同版本的 Nginx 的 proxy_cache 對(duì) Range 的處理情況。
Nginx-0.8.15
在 Nginx-0.8.15 中,使用如下配置文件做測(cè)試:
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;}} }重點(diǎn)說明以下兩種情況:
- 第一次 Range 請(qǐng)求(沒有本地緩存),Nginx 會(huì)去后端將整個(gè)文件拉取下來(后端響應(yīng)碼是200)后,并且返回給客戶端的是整個(gè)文件,響應(yīng)狀態(tài)碼是200,而非206. 后續(xù)的 Range 請(qǐng)求則都用緩存下來的本地文件提供服務(wù),且響應(yīng)狀態(tài)碼都是對(duì)應(yīng)的206了。
- 如果在上面的配置文件中,加上 proxy_set_header Range $http_range;再進(jìn)行測(cè)試(測(cè)試前先清空 Nginx 本地緩存)。則第一次 Range 請(qǐng)求(沒有本地緩存),Nginx 會(huì)去后端用 Range 請(qǐng)求文件,而不會(huì)把整個(gè)文件拉下來,響應(yīng)給客戶端的也是206.但問題在于,由于沒有把 Range 請(qǐng)求加入到 cache key 中,會(huì)導(dǎo)致后續(xù)所有的請(qǐng)求,不管 Range 如何,只要 url 不變,都會(huì)直接用cache 的內(nèi)容來返回給客戶端,這肯定是不符合要求的。
Nginx-1.9.7
在 Nginx-1.9.7 中,同樣進(jìn)行上面兩種情況的測(cè)試,第二種情況的結(jié)果其實(shí)是沒多少意義,而且肯定也和 Nginx-0.8.15 一樣,所以這里只關(guān)注第一種測(cè)試情況。
第一次 Range 請(qǐng)求(沒有本地緩存),Nginx 會(huì)去后端將整個(gè)文件拉取下來(后端響應(yīng)碼是200),但返回給客戶端的是正確的 Range 響應(yīng),即206.后續(xù)的 Range 請(qǐng)求,則都用緩存下來的本地文件提供服務(wù),且都是正常的206響應(yīng)。
可見,與之前的版本相比,還是有改進(jìn)的,但并沒有解決最實(shí)質(zhì)的問題。
我們可以看看 Nginx 官方對(duì)于 Cache 在 Range 請(qǐng)求時(shí)行為的說明:
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
我們繼續(xù)看看Nginx-1.9.8, 當(dāng)然,在編譯時(shí)要加上參數(shù)--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;}} }不測(cè)不知道,一側(cè)嚇一跳,這儼然是一個(gè)殺手級(jí)的特性。
首先,如果不帶 Range 請(qǐng)求,后端大文件在本地 cache 時(shí),會(huì)按照配置的 slice 大小進(jìn)行切片存儲(chǔ)。
其次,如果帶 Range 請(qǐng)求,則 Nginx 會(huì)用合適的 Range 大小(以 slice 為邊界)去后端請(qǐng)求,這個(gè)大小跟客戶端請(qǐng)求的 Range 可能不一樣,并將以 slice 為大小的切片存儲(chǔ)到本地,并以正確的206響應(yīng)客戶端。
注意上面所說的,Nginx 到后端的 Range 并不一定等于客戶端請(qǐng)求的 Range,因?yàn)闊o論你請(qǐng)求的Range 如何,Nginx 到后端總是以 slice 大小為邊界,將客戶端請(qǐng)求分割成若干個(gè)子請(qǐng)求到后端,假設(shè)配置的 slice 大小為1M,即1024字節(jié),那么如果客戶端請(qǐng)求 Range 為0-1023范圍以內(nèi)任何數(shù)字,均會(huì)落到第一個(gè)切片上,如果請(qǐng)求的 Range 橫跨了幾個(gè) slice 大小,則nginx會(huì)向后端發(fā)起多個(gè)子請(qǐng)求,將這幾個(gè) slice 緩存下來。而對(duì)客戶端,均以客戶端請(qǐng)求的 Range 為準(zhǔn)。如果一個(gè)請(qǐng)求中,有一部分文件之前沒有緩存下來,則 Nginx 只會(huì)去向后端請(qǐng)求缺失的那些切片。
由于這個(gè)模塊是建立在子請(qǐng)求的基礎(chǔ)上,會(huì)有這么一個(gè)潛在問題:當(dāng)文件很大或者 slice 很小的時(shí)候,會(huì)按照 slice 大小分成很多個(gè)子請(qǐng)求,而這些個(gè)子請(qǐng)求并不會(huì)馬上釋放自己的資源,可能會(huì)導(dǎo)致文件描述符耗盡等情況。
小結(jié)
總結(jié)一下,需要注意的點(diǎn):
- 該模塊用在 proxy_cache 大文件的場(chǎng)景,將大文件切片緩存
- 編譯時(shí)對(duì) configure 加上 --with-http_slice_module 參數(shù)
- $slice_range 一定要加到 proxy_cache_key 中,并使用 proxy_set_header 將其作為 Range 頭傳遞給后端
- 要根據(jù)文件大小合理設(shè)置 slice 大小
具體特性的說明,可以參考 Roman Arutyunyan 提出這個(gè) patch 時(shí)的郵件來往:
https://forum.nginx.org/read.php?29,261929,261929#msg-261929
順帶提一下,Roman Arutyunyan 也是個(gè)大牛,做流媒體領(lǐng)域的同學(xué)們肯定很多都聽說過:nginx-rtmp?模塊的作者。
參考資料
https://www.nginx.com/blog/nginx-caching-guide/
http://nginx.org/en/CHANGES
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
總結(jié)
以上是生活随笔為你收集整理的Nginx-1.9.8推出的切片模块的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RTMP协议从入门到放弃
- 下一篇: VT-x/AMD-V 硬件加速器已被启动