1. 程式人生 > >[轉]Nginx負載均衡原理初解

[轉]Nginx負載均衡原理初解

sockaddr lvs 設計 ews 設備 腳本 匹配 ocl 執行權

什麽是負載均衡

我們知道單臺服務器的性能是有上限的,當流量很大時,就需要使用多臺服務器來共同提供服務,這就是所謂的集群。

負載均衡服務器,就是用來把經過它的流量,按照某種方法,分配到集群中的各臺服務器上。這樣一來不僅可以承擔

更大的流量、降低服務的延遲,還可以避免單點故障造成服務不可用。一般的反向代理服務器,都具備負載均衡的功能。

負載均衡功能可以由硬件來提供,比如以前的F5設備。也可以由軟件來提供,LVS可以提供四層的負載均衡(利用IP和端口),

Haproxy和Nginx可以提供七層的負載均衡(利用應用層信息)。

來看一個最簡單的Nginx負載均衡配置。

[html]
view plain copy
  1. http {
  2. upstream cluster {
  3. server srv1;
  4. server srv2;
  5. server srv3;
  6. }
  7. server {
  8. listen 80;
  9. location / {
  10. proxy_pass http://cluster;
  11. }
  12. }
  13. }

通過上述配置,Nginx會作為HTTP反向代理,把訪問本機的HTTP請求,均分到後端集群的3臺服務器上。

此時使用的HTTP反向代理模塊是ngx_http_proxy_module。

一般在upstream配置塊中要指明使用的負載均衡算法,比如hash、ip_hash、least_conn。

這裏沒有指定,所以使用了默認的HTTP負載均衡算法 - 加權輪詢。

負載均衡流程圖

在描述負載均衡模塊的具體實現前,先來看下它的大致流程:

技術分享

負載均衡模塊

Nginx目前提供的負載均衡模塊:

ngx_http_upstream_round_robin,加權輪詢,可均分請求,是默認的HTTP負載均衡算法,集成在框架中。

ngx_http_upstream_ip_hash_module,IP哈希,可保持會話。

ngx_http_upstream_least_conn_module,最少連接數,可均分連接。

ngx_http_upstream_hash_module,一致性哈希,可減少緩存數據的失效。

以上負載均衡模塊的實現,基本上都遵循一套相似的流程。

1. 指令的解析函數

比如least_conn、ip_hash、hash指令的解析函數。

這些函數在解析配置文件時調用,主要用於:

檢查指令參數的合法性

指定peer.init_upstream函數指針的值,此函數用於初始化upstream塊。

2. 初始化upstream塊

在執行完指令的解析函數後,緊接著會調用所有HTTP模塊的init main conf函數。

在執行ngx_http_upstream_module的init main conf函數時,會調用所有upstream塊的初始化函數,

即在第一步中指定的peer.init_upstream,主要用於:

創建和初始化後端集群,保存該upstream塊的數據

指定peer.init,此函數用於初始化請求的負載均衡數據

來看下ngx_http_upstream_module。

[java] view plain copy
  1. ngx_http_module_t ngx_http_upstream_module_ctx = {
  2. ...
  3. ngx_http_upstream_init_main_conf, /* init main configuration */
  4. ...
  5. };
[java] view plain copy
  1. static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf)
  2. {
  3. ...
  4. /* 數組的元素類型是ngx_http_upstream_srv_conf_t */
  5. for (i = 0; i < umcf->upstreams.nelts; i++) {
  6. /* 如果沒有指定upstream塊的初始化函數,默認使用round robin的 */
  7. init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream :
  8. ngx_http_upstream_init_round_robin;
  9. if (init(cf, uscfp[i] != NGX_OK) {
  10. return NGX_CONF_ERROR;
  11. }
  12. }
  13. ...
  14. }

3. 初始化請求的負載均衡數據塊

當收到一個請求後,一般使用的反向代理模塊(upstream模塊)為ngx_http_proxy_module,

其NGX_HTTP_CONTENT_PHASE階段的處理函數為ngx_http_proxy_handler,在初始化upstream機制的

函數ngx_http_upstream_init_request中,調用在第二步中指定的peer.init,主要用於:

創建和初始化該請求的負載均衡數據塊

指定r->upstream->peer.get,用於從集群中選取一臺後端服務器(這是我們最為關心的)

指定r->upstream->peer.free,當不用該後端時,進行數據的更新(不管成功或失敗都調用)

請求的負載均衡數據塊中,一般會有一個成員指向對應upstream塊的數據,除此之外還會有自己獨有的成員。

"The peer initialization function is called once per request.
It sets up a data structure that the module will use as it tries to find an appropriate
backend server to service that request; this structure is persistent across backend re-tries,
so it‘s a convenient place to keep track of the number of connection failures, or a computed
hash value. By convention, this struct is called ngx_http_upstream_<module_name>_peer_data_t."

4. 選取一臺後端服務器

一般upstream塊中會有多臺後端,那麽對於本次請求,要選定哪一臺後端呢?

這時候第三步中r->upstream->peer.get指向的函數就派上用場了:

采用特定的算法,比如加權輪詢或一致性哈希,從集群中選出一臺後端,處理本次請求。

選定後端的地址保存在pc->sockaddr,pc為主動連接。

函數的返回值:

NGX_DONE:選定一個後端,和該後端的連接已經建立。之後會直接發送請求。

NGX_OK:選定一個後端,和該後端的連接尚未建立。之後會和後端建立連接。

NGX_BUSY:所有的後端(包括備份集群)都不可用。之後會給客戶端發送502(Bad Gateway)。

5. 釋放一臺後端服務器

當不再使用一臺後端時,需要進行收尾處理,比如統計失敗的次數。

這時候會調用第三步中r->upstream->peer.free指向的函數。

函數參數state的取值:

0,請求被成功處理

NGX_PEER_FAILED,連接失敗

NGX_PEER_NEXT,連接失敗,或者連接成功但後端未能成功處理請求

一個請求允許嘗試的後端數為pc->tries,在第三步中指定。當state為後兩個值時:

如果pc->tries不為0,需要重新選取一個後端,繼續嘗試,此後會重復調用r->upstream->peer.get。

如果pc->tries為0,便不再嘗試,給客戶端返回502錯誤碼(Bad Gateway)。

在upstream模塊的回調

負載均衡模塊的功能是從後端集群中選取一臺後端服務器,而具體的反向代理功能是由upstream模塊實現的,

比如和後端服務器建立連接、向後端服務器發送請求、處理後端服務器的響應等。

我們來看下負載均衡模塊提供的幾個鉤子函數,是在upstream模塊的什麽地方回調的。

Nginx的HTTP反向代理模塊為ngx_http_proxy_module,其NGX_HTTP_CONTENT_PHASE階段的處理函數為

ngx_http_proxy_handler,每個請求的upstream機制是從這裏開始的。

[java] view plain copy
  1. ngx_http_proxy_handler
  2. ngx_http_upstream_create /* 創建請求的upstream實例 */
  3. ngx_http_upstream_init /* 啟動upstream機制 */
  4. ngx_htp_upstream_init_request /* 負載均衡模塊的入口 */
  5. uscf->peer.init(r, uscf) /* 第三步,初始化請求的負載均衡數據塊 */
  6. ...
  7. ngx_http_upstream_connect /* 可能會被ngx_http_upstream_next重復調用 */
  8. ngx_event_connect_peer(&u->peer); /* 連接後端 */
  9. pc->get(pc, pc->data); /* 第四步,從集群中選取一臺後端 */
  10. ...
  11. /* 和後端建連成功後 */
  12. c = u->peer.connection;
  13. c->data = r;
  14. c->write->handler = ngx_http_upstream_handler; /* 註冊的連接的讀事件處理函數 */
  15. c->read->handler = ngx_http_upstream_handler; /* 註冊的連接的寫事件處理函數 */
  16. u->write_event_handler = ngx_http_upstream_send_request_handler; /* 寫事件的真正處理函數 */
  17. u->read_event_handler = ngx_http_upstream_process_header; /* 讀事件的真正處理函數 */

選定後端之後,在和後端通信的過程中如果發生了錯誤,會調用ngx_http_upstream_next來繼續嘗試其它的後端。

[java] view plain copy
  1. ngx_http_upstream_next
  2. if (u->peer.sockaddr) {
  3. if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403 ||
  4. ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404)
  5. state = NGX_PEER_NEXT;
  6. else
  7. state = NGX_PEER_FAILED;
  8. /* 第五步,釋放後端服務器 */
  9. u->peer.free(&u->peer, u->peer.data, state);
  10. u->peer.sockaddr = NULL;
  11. }

Reference

[1]. http://www.evanmiller.org/nginx-modules-guide.html#proxying

[2]. http://tengine.taobao.org/book/chapter_05.html#id5

---------------------

下載nginx源碼包,編譯nginx需要指定pcre,zlib,openssl,到官網上下載源代碼包:
http://www.zlib.NET/
http://www.openssl.org/
http://www.pcre.org/

將這三個包下載放到/opt目錄,tar -xzvf *.gz解壓,然後也將nginx-0.6.32的包解壓到/opt目錄下,進入nginx目錄,執行:
#./configure --sbin-path=/usr/local/sbin --with-http_ssl_module --with-pcre=../pcre-7.7 --with-zlib=../zlib-1.2.3 --with-openssl=../openssl-0.9.8g
#make && make install

如果在./configure時出現在錯誤,可能是沒有安裝gcc g+編譯器的緣故。單獨安裝這兩個編譯器比較麻煩,ubuntu提供了build-essential來安裝gcc和g++編譯器

apt-get install build-essential

采用aptitude安裝的時候,系統會將所有依賴的包都一並裝上,但是使 用源碼編譯的時候就沒這麽智能了,我們需要找到需要的依賴包,然後手工安裝,幸運的是,這並不復雜,也不多,例如pcre, ssl 和zlib,安裝方法比較簡單:

sudo aptitude install libpcre3 libpcre3-dev libpcrecpp0 libssl-dev zlib1g-dev

三、安裝Nginx

ok,準備工作做完了,是時候開始下載、 安裝Nginx了。

1、下載源代碼

到Nignx官網去下載源代碼

2、 解壓

tar -zxvf nginx-0.6.31.tar.gz
...
cd nginx-0.6.31/

3、選擇需要的編譯選項
Nginx的編譯選項還是不少 的,基本上都是定制位置和模塊,分別是:
1)--sbin-path=/usr/local/sbin
從源碼編譯Nginx會安裝在/usr/local/nginx目錄下(你可以使用參數--prefix=<path>指定自己需要的位置),然後會將bin文件放在/usr/local/nginx/sbin/nginx,雖然比較清晰,但是和我們一般的習慣不同

(作為程序員的我們更習慣在/usr/local /sbin下尋找bin文件);

2)--with-http_ssl_module
主要是提供https訪問支持的。

5、 Compile

./configure --sbin-path=/usr/local/sbin --with-http_ssl_module

編 譯的時間不會很長,完成的時候你會在屏幕上看到類似下面的信息:

...
nginx path prefix: "/usr/local/nginx"
nginx binary file: "/usr/local/sbin"
nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
...

留 意上面的提示,涉及到nginx的幾個重要文件的路徑信息。

6、Make && make install
差不多快完成了,現在make一下。

make

sudo make install

在使用make install 命令時可能會提示當前執行的命令權限不夠,sudo su命令然後輸入密碼提升下權限就ok

7、chmod(可選)
還需要給bin文件有執行權限,也比較簡單:

chmod +x /usr/local/sbin/nginx

8、Nginx重要文件安裝目錄

默認nginx安裝的目錄在/usr/local/nginx下,包括:
/usr/local/nginx/sbin #nginx啟動文件
/usr/local/nginx/conf #配置文件
/usr/local/nginx/html #默認網頁文件
/usr/local/nginx/logs #日誌文件

四、使用

安裝完成後,試著使用下。
1、啟動

sudo /usr/local/sbin/nginx

然後把自己的瀏覽器導航到http://IP就可 以看到默認的歡迎界面了

Welcome to nginx!

If you see this page, the nginx web server is successfully installed andworking. Further configuration is required.

For online documentation and support please refer tonginx.org.
Commercial support is available atnginx.com.

Thank you for using nginx.

2、停止

sudo /usr/local/sbin/nginx-s stop


六、附錄:Compile-time options

(原文參考:http://wiki.codemongers.com/NginxInstallOptions )

configure 腳本確定系統所具有一些特性,特別是 nginx 用來處理連接的方法。然後,它創建 Makefile 文件。

configure 支持下面的選項:

1)目錄屬性

--prefix=<path> - Nginx安裝路徑。如果沒有指定,默認為 /usr/local/nginx。

--sbin-path=<path> - Nginx可執行文件安裝路徑。只能安裝時指定,如果沒有指定,默認為<prefix>/sbin/nginx。

--conf-path=<path> - 在沒有給定-c選項下默認的nginx.conf的路徑。如果沒有指定,默認為<prefix>/conf/nginx.conf。

--pid-path=<path> - 在nginx.conf中沒有指定pid指令的情況下,默認的nginx.pid的路徑。如果沒有指定,默認為 <prefix>/logs/nginx.pid。

--lock-path=<path> - nginx.lock文件的路徑,默認為<prefix>/logs/nginx.lock

--error-log-path=<path> - 在nginx.conf中沒有指定error_log指令的情況下,默認的錯誤日誌的路徑。如果沒有指定,默認為 <prefix>/logs/error.log。

--http-log-path=<path> - 在nginx.conf中沒有指定access_log指令的情況下,默認的訪問日誌的路徑。如果沒有指定,默認為 <prefix>/logs/access.log。

--user=<user> - 在nginx.conf中沒有指定user指令的情況下,默認的nginx使用的用戶。如果沒有指定,默認為 nobody。

--group=<group> - 在nginx.conf中沒有指定user指令的情況下,默認的nginx使用的組。如果沒有指定,默認為 nobody。

--builddir=DIR - 指定編譯的目錄

2)模塊

--with-rtsig_module - 啟用 rtsig 模塊

--with-select_module --without-select_module -允許或不允許開啟SELECT模式,如果 configure 沒有找到更合適的模式,比如:kqueue(sun os),epoll (Linux kenel 2.6+), rtsig(實時信號)或者/dev/poll(一種類似select的模式,底層實現與SELECT基本相 同,都是采用輪訓方法) SELECT模式將是默認安裝模式

--with-poll_module --without-poll_module - Whether or not to enable the poll module. This module is enabled by default if a more suitable method such as kqueue, epoll, rtsig or /dev/poll is not discovered by configure.

--with-http_ssl_module -開啟HTTP SSL模塊,使NGINX可以支持HTTPS請求。這個模塊需要已經安裝了OPENSSL,在DEBIAN上是libssl

--with-http_realip_module - 啟用 ngx_http_realip_module

--with-http_addition_module - 啟用 ngx_http_addition_module

--with-http_sub_module - 啟用 ngx_http_sub_module

--with-http_dav_module - 啟用 ngx_http_dav_module

--with-http_flv_module - 啟用 ngx_http_flv_module

--with-http_stub_status_module - 啟用 "server status" 頁

--without-http_charset_module - 禁用 ngx_http_charset_module

--without-http_gzip_module - 禁用 ngx_http_gzip_module. 如果啟用,需要 zlib 。

--without-http_ssi_module - 禁用 ngx_http_ssi_module

--without-http_userid_module - 禁用 ngx_http_userid_module

--without-http_access_module - 禁用 ngx_http_access_module

--without-http_auth_basic_module - 禁用 ngx_http_auth_basic_module

--without-http_autoindex_module - 禁用 ngx_http_autoindex_module

--without-http_geo_module - 禁用 ngx_http_geo_module

--without-http_map_module - 禁用 ngx_http_map_module

--without-http_referer_module - 禁用 ngx_http_referer_module

--without-http_rewrite_module - 禁用 ngx_http_rewrite_module. 如果啟用需要 PCRE 。

--without-http_proxy_module - 禁用 ngx_http_proxy_module

--without-http_fastcgi_module - 禁用 ngx_http_fastcgi_module

--without-http_memcached_module - 禁用 ngx_http_memcached_module

--without-http_limit_zone_module - 禁用 ngx_http_limit_zone_module

--without-http_empty_gif_module - 禁用 ngx_http_empty_gif_module

--without-http_browser_module - 禁用 ngx_http_browser_module

--without-http_upstream_ip_hash_module - 禁用 ngx_http_upstream_ip_hash_module

--with-http_perl_module - 啟用 ngx_http_perl_module

--with-perl_modules_path=PATH - 指定 perl 模塊的路徑

--with-perl=PATH - 指定 perl 執行文件的路徑

--http-log-path=PATH - Set path to the http access log

--http-client-body-temp-path=PATH - Set path to the http client request body temporary files

--http-proxy-temp-path=PATH - Set path to the http proxy temporary files

--http-fastcgi-temp-path=PATH - Set path to the http fastcgi temporary files

--without-http - 禁用 HTTP server

--with-mail - 啟用 IMAP4/POP3/SMTP 代理模塊

--with-mail_ssl_module - 啟用 ngx_mail_ssl_module

--with-cc=PATH - 指定 C 編譯器的路徑

--with-cpp=PATH - 指定 C 預處理器的路徑

--with-cc-opt=OPTIONS - Additional parameters which will be added to the variable CFLAGS. With the use of the system library PCRE in FreeBSD, it is necessary to indicate --with-cc-opt="-I /usr/local/include". If we are using select() and it is necessary to increase the number of file descriptors, then this also can be assigned here: --with-cc-opt="-D FD_SETSIZE=2048".

--with-ld-opt=OPTIONS - Additional parameters passed to the linker. With the use of the system library PCRE in FreeBSD, it is necessary to indicate --with-ld-opt="-L /usr/local/lib".

--with-cpu-opt=CPU - 為特定的 CPU 編譯,有效的值包括:pentium, pentiumpro, pentium3, pentium4, athlon, opteron, amd64, sparc32, sparc64, ppc64

--without-pcre - 禁止 PCRE 庫的使用。同時也會禁止 HTTP rewrite 模塊。在 "location" 配置指令中的正則表達式也需要 PCRE 。

--with-pcre=DIR - 指定 PCRE 庫的源代碼的路徑。

--with-pcre-opt=OPTIONS - Set additional options for PCRE building.

--with-md5=DIR - Set path to md5 library sources.

--with-md5-opt=OPTIONS - Set additional options for md5 building.

--with-md5-asm - Use md5 assembler sources.

--with-sha1=DIR - Set path to sha1 library sources.

--with-sha1-opt=OPTIONS - Set additional options for sha1 building.

--with-sha1-asm - Use sha1 assembler sources.

--with-zlib=DIR - Set path to zlib library sources.

--with-zlib-opt=OPTIONS - Set additional options for zlib building.

--with-zlib-asm=CPU - Use zlib assembler sources optimized for specified CPU, valid values are: pentium, pentiumpro

--with-openssl=DIR - Set path to OpenSSL library sources

--with-openssl-opt=OPTIONS - Set additional options for OpenSSL building

--with-debug - 啟用調試日誌

--add-module=PATH - Add in a third-party module found in directory PAT

---------------------

【轉】nginx proxy_cache 緩存配置

前言:

由於本人工作原因,涉及到網絡直播領域,其中視頻的回放下載,涉及到了一些視頻下載方面的技術。針對於一個完整視頻的下載,目前市面上的主流做法是,先將整個視頻流切片,存儲到文件服務器中,在用戶需要觀看回放視頻時。通過一個視頻回源服務器,去文件服務器中逐個請求切片,返回給用戶播放。 今天著重探討的是關於回源服務器緩存的配置以及合理的緩存策略。

通過給回源服務器配置緩存的案例,詳細講解一整套緩存配置機制,並且可沿用到其他任何緩存配置場景中。

今天的講解分為四點:
  • 回源服務器的工作是啥
  • 為啥需要給回源服務器加緩存
  • 如何配置緩存
  • 如何針對業務場景配置完備的緩存機制

回源服務器的工作:

回源服務器在下面敘述中簡稱:源站 如圖所示,在文件下載的過程中,橫跨在cdn與文件服務器之間,作為下載樞紐。 技術分享

源站架構:源站是nginx+PHP的webserver架構,如圖所示:

技術分享

但如果源站只是簡單的收到請求,然後下載資源,再返回,勢必會存在以下幾點不夠優化的問題: 1、cdn可能存在多次回源現象 2、源站對同一資源的多次下載,存在網絡流量帶寬浪費,以及不必要的耗時。 所以為了優化這些問題,需要給源站做一層緩存。緩存策略采用nginx自帶的proxy_cache模塊。

proxy_cache原理:

proxy_cache模塊的工作原理如圖所示: 技術分享

如何配置proxy_cache模塊

在nginx.conf文件中添加如下代碼: [plain] view plain copy
  1. http{
  2. ......
  3. proxy_cache_path/data/nginx/tmp-test levels=1:2 keys_zone=tmp-test:100m inactive=7d max_size=1000g;
  4. }
代碼說明:

proxy_cache_path 緩存文件路徑

levels 設置緩存文件目錄層次;levels=1:2 表示兩級目錄

keys_zone 設置緩存名字和共享內存大小

inactive 在指定時間內沒人訪問則被刪除

max_size 最大緩存空間,如果緩存空間滿,默認覆蓋掉緩存時間最長的資源。

當配置好之後,重啟nginx,如果不報錯,則配置的proxy_cache會生效

查看 proxy_cache_path /data/nginx/目錄, 會發現生成了tmp-test文件夾。

如何使用proxy_cache

在你對應的nginx vhost server配置文件中添加如下代碼: [plain] view plain copy
  1. location /tmp-test/ {
  2. proxy_cache tmp-test;
  3. proxy_cache_valid 200 206 304 301 302 10d;
  4. proxy_cache_key $uri;
  5. proxy_set_header Host $host:$server_port;
  6. proxy_set_header X-Real-IP $remote_addr;
  7. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  8. proxy_passhttp://127.0.0.1:8081/media_store.php/tmp-test/;
  9. }
配置項介紹: 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存取

proxy_set_header 自定義http header頭,用於發送給後端真實服務器。

proxy_pass 指代理後轉發的路徑,註意是否需要最後的/ 到這裏,最基本的proxy_cache功能就配置成功了。當uri成功匹配到該location,則proxy_cache就會生效。

添加proxy_cache之後,請求過程的變化:

1、第一次訪問: 技術分享 第一次訪問,proxy_cache並沒有找到對應的緩存文件(未命中緩存MISS),所以當第一次請求完成的同時,proxy_cache會保持緩存: 2、保存緩存,如圖所示: 技術分享 3、同一個url第二次訪問,當同一個文件再次到達源站,proxy_cache就會找到其對應的緩存文件(命中緩存HIT)直接返回給請求端,無需再執行php程序,如圖所示: 技術分享

提出疑問:

到此,就完成了最基本的proxy_cache配置和訪問過程介紹,但是最基本的配置,往往無法滿足我們的業務需求,我們往往會提出以下幾點疑問和需求:
  1. 需要主動清理緩存文件
  2. 寫入路徑為一塊磁盤,如果磁盤打滿該怎麽解決?
  3. 如何讓源站支持斷點續傳,以及斷點續傳的緩存策略
  4. 如果請求端 range 請求(分片下載)一個大資源,同樣的uri,如何區別請求?
  5. 還需要告訴請求端,資源的過期時間
  6. 日誌統計,如何配置命中與不命中字段,如何做統計?
面對以上疑問,我們一個一個解決。

問題一:主動清理緩存

采用:nginx proxy_cache_purge 模塊 ,該模塊與proxy_cache成對出現,功能正好相反。 設計方法:在nginx中,另啟一個server,當需要清理響應資源的緩存時,在本機訪問這個server。 例如: 訪問 127.0.0.1:8083/tmp-test/TL39ef7ea6d8e8d48e87a30c43b8f75e30.txt 即可清理該資源的緩存文件。 配置方法: [plain] view plain copy
  1. location /tmp-test/ {
  2. allow 127.0.0.1; //只允許本機訪問
  3. deny all; //禁止其他所有ip
  4. proxy_cache_purge tmp-test $uri; //清理緩存
  5. }
proxy_cache_purge:緩存清理模塊 tmp-test:指定的key_zone $uri:指定的生成key的參數 proxy_cache_purge緩存清理過程,如圖所示: 技術分享

問題二:緩存文件強磁盤打滿該怎麽辦?

由於寫入路徑為一個單一目錄,只能寫入一塊磁盤。一塊磁盤很快就會被打滿,解決該問題有如下兩種方法: 1、將多塊磁盤做磁盤陣列? 缺點是:減小了實際的存儲空間。 2、巧妙得運用proxy_cache_path的目錄結構,由於levels=1:2,這導致緩存文件的目錄結構為兩層,每層目錄名,都是由hash函數生成。如圖所示: 技術分享 總共含有16*16*16=4096個文件目錄。對該一級目錄進行軟連接,分別將0-f軟連接到你所需要的指定磁盤目錄上,如圖所示: 技術分享 通過軟鏈的方法,實現:將不同盤下的目錄作為真正存放數據的路徑,解決了多盤利用,單盤被打滿的問題。

問題三:支持range(斷點續傳)

添加上緩存代理之後,客戶端發起的range請求將會失效,如下圖所示: 技術分享 導致range參數無法傳遞到下一級的原因如下: 當緩存代理轉發http請求到後端服務器時,http header會改變,header中的部分參數,會被取消掉。其中range參數被取消,導致,後端nginx服務器沒有收到range參數,最終導致這個分片下載不成功。所以需要對代理轉發的header進行配置。 例如: [plain] view plain copy
  1. location /tmp-test/ {
  2. proxy_cache tmp-test;
  3. proxy_cache_valid 200 206 304 301 302 10d;
  4. proxy_cache_key $uri;
  5. <span style="color:#ff0000;">proxy_set_header Range $http_range;</span>
  6. proxy_pass http://127.0.0.1:8081/media_store.php/tmp-test/;
  7. }
紅色部分的含義:將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配置即可解決: [plain] view plain copy
  1. location /media_store.php {
  2. fastcgi_pass 127.0.0.1:9000;
  3. fastcgi_index media_store.php;
  4. fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
  5. include fastcgi_params;
  6. if ( $http_range = ‘‘){
  7. expires 2592000s;
  8. }
  9. }
在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日誌截圖: 技術分享

總結:

整個一套完備的緩存策略就介紹到此,這套方案中不僅實現了基本的緩存配置,還解決了實際場景應用中會遇到的,磁盤擴展,緩存清理,斷點續傳,緩存過期時間,緩存命中提示等問題,只要將這套方案靈活運用,不管是再復雜的場景,基本都能滿足需求。以上都是我在工作中爬過的坑,不斷完善總結出的結果,希望對讀者能有幫助。

[轉]Nginx負載均衡原理初解