因爲本人工做緣由,涉及到網絡直播領域,其中視頻的回放下載,涉及到了一些視頻下載方面的技術。針對於一個完整視頻的下載,目前市面上的主流作法是,先將整個視頻流切片,存儲到文件服務器中,在用戶須要觀看回放視頻時。經過一個視頻回源服務器,去文件服務器中逐個請求切片,返回給用戶播放。
在nginx.conf文件中添加以下代碼:
- http{
- ......
- proxy_cache_path/data/nginx/tmp-test levels=1:2 keys_zone=tmp-test:100m inactive=7d max_size=1000g;
- }
代碼說明:
proxy_cache_path 緩存文件路徑web
levels 設置緩存文件目錄層次;levels=1:2 表示兩級目錄後端
keys_zone 設置緩存名字和共享內存大小緩存
inactive 在指定時間內沒人訪問則被刪除服務器
max_size 最大緩存空間,若是緩存空間滿,默認覆蓋掉緩存時間最長的資源。
當配置好以後,重啓nginx,若是不報錯,則配置的proxy_cache會生效網絡
查看 proxy_cache_path /data/nginx/目錄,
會發現生成了tmp-test文件夾。
如何使用proxy_cache
在你對應的nginx vhost server配置文件中添加以下代碼:
- location /tmp-test/ {
- proxy_cache tmp-test;
- proxy_cache_valid 200 206 304 301 302 10d;
- proxy_cache_key $uri;
- proxy_set_header Host $host:$server_port;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_passhttp://127.0.0.1:8081/media_store.php/tmp-test/;
- }
配置項介紹:
Proxy_cache tmp-test 使用名爲tmp-test的對應緩存配置
proxy_cache_valid 200 206 304 301 302 10d; 對httpcode爲200…的緩存10天架構
proxy_cache_key $uri 定義緩存惟一key,經過惟一key來進行hash存取ssh
proxy_set_header 自定義http header頭,用於發送給後端真實服務器。函數
proxy_pass 指代理後轉發的路徑,注意是否須要最後的/
到這裏,最基本的proxy_cache功能就配置成功了。當uri成功匹配到該location,則proxy_cache就會生效。
添加proxy_cache以後,請求過程的變化:
一、第一次訪問:
第一次訪問,proxy_cache並無找到對應的緩存文件(未命中緩存MISS),因此當第一次請求完成的同時,proxy_cache會保持緩存:
二、保存緩存,如圖所示:
三、同一個url第二次訪問,當同一個文件再次到達源站,proxy_cache就會找到其對應的緩存文件(命中緩存HIT)直接返回給請求端,無需再執行php程序,如圖所示:
提出疑問:
到此,就完成了最基本的proxy_cache配置和訪問過程介紹,可是最基本的配置,每每沒法知足咱們的業務需求,咱們每每會提出如下幾點疑問和需求:
- 須要主動清理緩存文件
- 寫入路徑爲一塊磁盤,若是磁盤打滿該怎麼解決?
- 如何讓源站支持斷點續傳,以及斷點續傳的緩存策略
- 若是請求端 range 請求(分片下載)一個大資源,一樣的uri,如何區別請求?
- 還須要告訴請求端,資源的過時時間
- 日誌統計,如何配置命中與不命中字段,如何作統計?
面對以上疑問,咱們一個一個解決。
問題一:主動清理緩存
採用:nginx proxy_cache_purge 模塊 ,該模塊與proxy_cache成對出現,功能正好相反。
設計方法:在nginx中,另啓一個server,當須要清理響應資源的緩存時,在本機訪問這個server。
例如:
訪問 127.0.0.1:8083/tmp-test/TL39ef7ea6d8e8d48e87a30c43b8f75e30.txt 便可清理該資源的緩存文件。
配置方法:
- location /tmp-test/ {
- allow 127.0.0.1; //只容許本機訪問
- deny all; //禁止其餘全部ip
- proxy_cache_purge tmp-test $uri; //清理緩存
- }
proxy_cache_purge:緩存清理模塊
tmp-test:指定的key_zone
$uri:指定的生成key的參數
proxy_cache_purge緩存清理過程,如圖所示:
問題二:緩存文件強磁盤打滿該怎麼辦?
因爲寫入路徑爲一個單一目錄,只能寫入一塊磁盤。一塊磁盤很快就會被打滿,解決該問題有以下兩種方法:
一、將多塊磁盤作磁盤陣列? 缺點是:減少了實際的存儲空間。
二、巧妙得運用proxy_cache_path的目錄結構,因爲levels=1:2,這致使緩存文件的目錄結構爲兩層,每層目錄名,都是由hash函數生成。如圖所示:
總共含有16*16*16=4096個文件目錄。對該一級目錄進行軟鏈接,分別將0-f軟鏈接到你所須要的指定磁盤目錄上,如圖所示:
經過軟鏈的方法,實現:將不一樣盤下的目錄做爲真正存放數據的路徑,解決了多盤利用,單盤被打滿的問題。
問題三:支持range(斷點續傳)
添加上緩存代理以後,客戶端發起的range請求將會失效,以下圖所示:
致使range參數沒法傳遞到下一級的緣由以下:
當緩存代理轉發http請求到後端服務器時,http header會改變,header中的部分參數,會被取消掉。其中range參數被取消,致使,後端nginx服務器沒有收到range參數,最終致使這個分片下載不成功。因此須要對代理轉發的header進行配置。
例如:
- location /tmp-test/ {
- proxy_cache tmp-test;
- proxy_cache_valid 200 206 304 301 302 10d;
- proxy_cache_key $uri;
- <span style="color:#ff0000;">proxy_set_header Range $http_range;</span>
- proxy_pass http://127.0.0.1:8081/media_store.php/tmp-test/;
- }
紅色部分的含義:將http請求中的range值($http_range)放到代理轉發的http請求頭中做爲參數range的值。
問題四,當支持range加載後,proxy_cache_key,則須要從新配置:
若是請求端 Range請求(分片下載)一個大資源,一樣的uri,proxy cache如何識別資源對應的key。
因爲nginx配置爲:proxy_cache_key $uri,用uri做爲key
因此當請求爲普通請求和range請求時,都是一樣的uri做爲key。proxy_cache將有可能致使錯誤返回。以下圖所示:
解決方法以下:
修改proxy_cache_key ,配置proxy_cache_key $http_range$uri;
這樣就能解決:key惟一性。能夠避免不論是正常請求仍是不一樣的range請求,第一次獲取的內容和以後獲取的緩存內容都不會出現異常。
問題五:如何配置-返回過時時間
須要經過返回過時時間來指定請求端,哪些資源須要緩存,哪些資源不緩存,
參數 |
正常請求 |
range請求 |
返回過時時間 |
返回 |
不返回 |
爲了防止請求端將分片資源當作完整資源緩存起來,咱們須要對正常請求,返回過時時間;對range請求, 不返回過時時間。
解決該問題,經過對nginx配置便可解決:
- location /media_store.php {
- fastcgi_pass 127.0.0.1:9000;
- fastcgi_index media_store.php;
- fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
- include fastcgi_params;
- if ( $http_range = ''){
- expires 2592000s;
- }
- }
在proxy_pass代理以後的location中加入對$http_range的判斷,expires 表示過時時間。 2592000s指緩存過時時間。
問題七:緩存命中狀況如何在http頭中體現,以及在nginx日誌中查看
解決方法:
利用nginx $upstream_cache_status變量:該變量表明緩存命中的狀態,
若是命中,爲HIT;若是未命中,爲MISS
在返回nginx server配置中添加:
add_header Nginx-Cache "$upstream_cache_status";
在nginxlog中添加:
log_format combinedio …$upstream_cache_status;
http返回head截圖:
nginx log日誌截圖:
總結:
整個一套完備的緩存策略就介紹到此,這套方案中不只實現了基本的緩存配置,還解決了實際場景應用中會遇到的,磁盤擴展,緩存清理,斷點續傳,緩存過時時間,緩存命中提示等問題,只要將這套方案靈活運用,不論是再複雜的場景,基本都能知足需求。以上都是我在工做中爬過的坑,不斷完善總結出的結果,但願對讀者能有幫助。