nginx 入門指北

nginx 介紹

Nginx 是俄羅斯人編寫的十分輕量級的 HTTP 服務器,Nginx,它的發音爲“engine X” ,是一個高性能的HTTP和反向代理服務器,同時也是一個 IMAP/POP3/SMTP 代理服務器

nginx 特點

Nginx 做爲 HTTP 服務器,有以下幾項基本特性

  • 處理靜態文件,索引文件以及自動索引;打開文件描述符緩衝.
  • 無緩存的反向代理加速,簡單的負載均衡和容錯.
  • FastCGI,簡單的負載均衡和容錯.
  • 模塊化的結構。包括 gzipping, byte ranges, chunked responses,以及 SSI-filter 等 filter。如果由 FastCGI 或其它代理服務器處理單頁中存在的多個 SSI,則這項處理可以並行運行,而不需要相互等待。
  • 支持 SSL 和 TLSSNI.

Nginx 具有很高的穩定性。其它 HTTP 服務器,當遇到訪問的峯值,或者有人惡意發起慢速連接時,也很可能會導致服務器物理內存耗盡頻繁交換,失去響應,只能重啓服務器。例如當前 apache 一旦上到 200 個以上進程,web響應速度就明顯非常緩慢了。而 Nginx 採取了分階段資源分配技術,使得它的 CPU 與內存佔用率非常低。Nginx 官方表示保持 10,000 個沒有活動的連接,它只佔 2.5M 內存,所以類似 DOS 這樣的攻擊對 Nginx 來說基本上是毫無用處的。就穩定性而言,Nginx 比 lighthttpd 更勝一籌。

Nginx 支持熱部署。它的啓動特別容易, 並且幾乎可以做到 7*24 不間斷運行,即使運行數個月也不需要重新啓動。你還能夠在不間斷服務的情況下,對軟件版本進行進行升級。

Nginx 採用 master-slave 模型,能夠充分利用 SMP 的優勢,且能夠減少工作進程在磁盤 I/O 的阻塞延遲。當採用 select()/poll() 調用時,還可以限制每個進程的連接數。

Nginx 代碼質量非常高,代碼很規範,手法成熟,模塊擴展也很容易。特別值得一提的是強大的 Upstream 與 Filter 鏈。Upstream 爲諸如 reverse proxy,與其他服務器通信模塊的編寫奠定了很好的基礎。而 Filter 鏈最酷的部分就是各個 filter 不必等待前一個 filter 執行完畢。它可以把前一個 filter 的輸出做爲當前 filter 的輸入,這有點像 Unix 的管線。這意味着,一個模塊可以開始壓縮從後端服務器發送過來的請求,且可以在模塊接收完後端服務器的整個請求之前把壓縮流轉向客戶端。

Nginx 採用了一些 os 提供的最新特性如對 sendfile (Linux2.2+),accept-filter (FreeBSD4.1+),TCP_DEFER_ACCEPT (Linux 2.4+)的支持,從而大大提高了性能。

初入nginx

Nginx 在啓動後,在 unix 系統中會以 daemon 的方式在後臺運行,後臺進程包含一個 master 進程和多個 worker 進程,所以,我們可以看到,Nginx 是以多進程的方式來工作的。
衆所周知,Nginx 性能高,而 Nginx 的高性能與其架構是分不開的。那麼 Nginx 究竟是怎麼樣的呢?這一節我們先來初識一下 Nginx 框架吧。

Nginx 在啓動後,在 unix 系統中會以 daemon 的方式在後臺運行,後臺進程包含一個 master 進程和多個 worker 進程。我們也可以手動地關掉後臺模式,讓 Nginx 在前臺運行,並且通過配置讓 Nginx 取消 master 進程,從而可以使 Nginx 以單進程方式運行。很顯然,生產環境下我們肯定不會這麼做,所以關閉後臺模式,一般是用來調試用的,在後面的章節裏面,我們會詳細地講解如何調試 Nginx。所以,我們可以看到,Nginx 是以多進程的方式來工作的,當然 Nginx 也是支持多線程的方式的,只是我們主流的方式還是多進程的方式,也是 Nginx 的默認方式。Nginx 採用多進程的方式有諸多好處,所以我就主要講解 Nginx 的多進程模式吧。

剛纔講到,Nginx 在啓動後,會有一個 master 進程和多個 worker 進程。master 進程主要用來管理 worker 進程,包含:接收來自外界的信號,向各 worker 進程發送信號,監控 worker 進程的運行狀態,當 worker 進程退出後(異常情況下),會自動重新啓動新的 worker 進程。而基本的網絡事件,則是放在 worker 進程中來處理了。多個 worker 進程之間是對等的,他們同等競爭來自客戶端的請求,各進程互相之間是獨立的。一個請求,只可能在一個 worker 進程中處理,一個 worker 進程,不可能處理其它進程的請求。worker 進程的個數是可以設置的,一般我們會設置與機器cpu核數一致,這裏面的原因與 Nginx 的進程模型以及事件處理模型是分不開的。Nginx 的進程模型,可以由下圖來表示:

一個連接請求過來,每個進程都有可能處理這個連接,怎麼做到的呢?

首先,每個 worker 進程都是從 master 進程 fork 過來,在 master 進程裏面,先建立好需要 listen 的 socket(listenfd)之後,然後再 fork 出多個 worker 進程。所有 worker 進程的 listenfd 會在新連接到來時變得可讀,爲保證只有一個進程處理該連接,所有 worker 進程在註冊 listenfd 讀事件前搶 accept_mutex,搶到互斥鎖的那個進程註冊 listenfd 讀事件,在讀事件裏調用 accept 接受該連接。當一個 worker 進程在 accept 這個連接之後,就開始讀取請求,解析請求,處理請求,產生數據後,再返回給客戶端,最後才斷開連接,這樣一個完整的請求就是這樣的了。我們可以看到,一個請求,完全由 worker 進程來處理,而且只在一個 worker 進程中處理。

Nginx 採用多 worker 的方式來處理請求,每個 worker 裏面只有一個主線程,那能夠處理的併發數很有限啊,多少個 worker 就能處理多少個併發,何來高併發呢?

非也,這就是 Nginx 的高明之處,Nginx 採用了異步非阻塞的方式來處理請求,具體到系統調用就是像 select/poll/epoll/kqueue 這樣的系統調用。它們提供了一種機制,讓你可以同時監控多個事件,調用他們是阻塞的,但可以設置超時時間,在超時時間之內,如果有事件準備好了,就返回。這種機制正好解決了我們上面的兩個問題,拿 epoll 爲例(在後面的例子中,我們多以 epoll 爲例子,以代表這一類函數),當事件沒準備好時,放到 epoll 裏面,事件準備好了,我們就去讀寫,當讀寫返回 EAGAIN 時,我們將它再次加入到 epoll 裏面。這樣,只要有事件準備好了,我們就去處理它,只有當所有事件都沒準備好時,纔在 epoll 裏面等着。這樣,我們就可以併發處理大量的併發了。

舉例來說:

同樣的4個進程,如果採用一個進程負責一個request的方式;那麼,同時進來4個request之後,每個進程就負責其中一個,直至會話關閉。期間,如果有第5個request進來了。就無法及時反應了,因爲4個進程都沒幹完活呢,因此,一般有個調度進程,每當新進來了一個request,就新開個進程來處理。(apche 行爲)

nginx不這樣,每進來一個request,會有一個worker進程去處理。但不是全程的處理,處理到什麼程度呢?處理到可能發生阻塞的地方。比如向後端服務器轉發request,並等待請求返回。那麼,這個處理的worker不會這麼傻等着,他會在發送完請求後,註冊一個事件:“如果upstream返回了,告訴我一聲,我再接着幹”。於是他就休息去了。此時,如果再有request 進來,他就可以很快再按這種方式處理。而一旦上游服務器返回了,就會觸發這個事件,worker纔會來接手,這個request纔會接着往下走。(nginx 行爲)

nginx 的一個生命週期

首先,Nginx 在啓動時,會解析配置文件,得到需要監聽的端口與 ip 地址,然後在 Nginx 的 master 進程裏面,先初始化好這個監控的 socket(創建 socket,設置 addrreuse 等選項,綁定到指定的 ip 地址端口,再 listen),然後再 fork 出多個子進程出來,然後子進程會競爭 accept 新的連接。此時,客戶端就可以向 Nginx 發起連接了。當客戶端與服務端通過三次握手建立好一個連接後,Nginx 的某一個子進程會 accept 成功,得到這個建立好的連接的 socket,然後創建 Nginx 對連接的封裝,即 ngx_connection_t 結構體。接着,設置讀寫事件處理函數並添加讀寫事件來與客戶端進行數據的交換。最後,Nginx 或客戶端來主動關掉連接,到此,一個連接就壽終正寢了。

  • 當然,Nginx 也是可以作爲客戶端來請求其它 server 的數據的(如 upstream 模塊),此時,與其它 server 創建的連接,也封裝在 ngx_connection_t 中。作爲客戶端,Nginx 先獲取一個 ngx_connection_t 結構體,然後創建 socket,並設置 socket 的屬性( 比如非阻塞)。然後再通過添加讀寫事件,調用 connect/read/write 來調用連接,最後關掉連接,並釋放 ngx_connection_t。

nginx 最大連接數

每個進程會有一個連接數的最大上限,這個上限與系統對 epoll fd 的限制不一樣。在操作系統中,通過 ulimit -n,我們可以得到一個進程所能夠打開的 epoll fd的最大數,即 nofile,因爲每個 socket 連接會佔用掉一個 fd,所以這也會限制我們進程的最大連接數,當然也會直接影響到我們程序所能支持的最大併發數,當 fd 用完後,再創建 socket 時,就會失敗。Nginx 通過設置 worker_connectons 來設置每個進程支持的最大連接數。如果該值大於 nofile,那麼實際的最大連接數是 nofile,Nginx 會有警告。Nginx 在實現時,是通過一個連接池來管理的,每個 worker 進程都有一個獨立的連接池,連接池的大小是 worker_connections。這裏的連接池裏面保存的其實不是真實的連接,它只是一個 worker_connections 大小的一個 ngx_connection_t 結構的數組。並且,Nginx 會通過一個鏈表 free_connections 來保存所有的空閒 ngx_connection_t,每次獲取一個連接時,就從空閒連接鏈表中獲取一個,用完後,再放回空閒連接鏈表裏面。
在這裏,很多人會誤解 worker_connections 這個參數的意思,認爲這個值就是 Nginx 所能建立連接的最大值。其實不然,這個值是表示每個 worker 進程所能建立連接的最大值,所以,一個 Nginx 能建立的最大連接數,應該是worker_connections * worker_processes。當然,這裏說的是最大連接數,對於 HTTP 請求本地資源來說,能夠支持的最大併發數量是worker_connections * worker_processes,而如果是 HTTP 作爲反向代理來說,最大併發數量應該是worker_connections * worker_processes/2。因爲作爲反向代理服務器,每個併發會建立與客戶端的連接和與後端服務的連接,會佔用兩個連接。

nginx 公平控制woker 進程的連接數

我們前面有說過一個客戶端連接過來後,多個空閒的進程,會競爭這個連接,很容易看到,這種競爭會導致不公平,如果某個進程得到 accept 的機會比較多,它的空閒連接很快就用完了,如果不提前做一些控制,當 accept 到一個新的 tcp 連接後,因爲無法得到空閒連接,而且無法將此連接轉交給其它進程,最終會導致此 tcp 連接得不到處理,就中止掉了。很顯然,這是不公平的,有的進程有空餘連接,卻沒有處理機會,有的進程因爲沒有空餘連接,卻人爲地丟棄連接。那麼,如何解決這個問題呢?首先,Nginx 的處理得先打開 accept_mutex 選項,此時,只有獲得了 accept_mutex 的進程纔會去添加accept事件,也就是說,Nginx會控制進程是否添加 accept 事件。Nginx 使用一個叫 ngx_accept_disabled 的變量來控制是否去競爭 accept_mutex 鎖。在第一段代碼中,計算 ngx_accept_disabled 的值,這個值是 Nginx 單進程的所有連接總數的八分之一,減去剩下的空閒連接數量,得到的這個 ngx_accept_disabled 有一個規律,當剩餘連接數小於總連接數的八分之一時,其值才大於 0,而且剩餘的連接數越小,這個值越大。再看第二段代碼,當 ngx_accept_disabled 大於 0 時,不會去嘗試獲取 accept_mutex 鎖,並且將 ngx_accept_disabled 減 1,於是,每次執行到此處時,都會去減 1,直到小於 0。不去獲取 accept_mutex 鎖,就是等於讓出獲取連接的機會,很顯然可以看出,當空餘連接越少時,ngx_accept_disable 越大,於是讓出的機會就越多,這樣其它進程獲取鎖的機會也就越大。不去 accept,自己的連接就控制下來了,其它進程的連接池就會得到利用,這樣,Nginx 就控制了多進程間連接的平衡了。

nginx 配置

配置指令是一個字符串,可以用單引號或者雙引號括起來,也可以不括。但是如果配置指令包含空格,一定要引起來。

指令上下文

nginx.conf 中的配置信息,根據其邏輯上的意義,對它們進行了分類,也就是分成了多個作用域,或者稱之爲配置指令上下文。不同的作用域含有一個或者多個配置項。

當前 Nginx 支持的幾個指令上下文:

  • main: Nginx 在運行時與具體業務功能(比如http服務或者email服務代理)無關的一些參數,比如工作進程數,運行的身份等。
  • http: 與提供 http 服務相關的一些配置參數。例如:是否使用 keepalive 啊,是否使用gzip進行壓縮等。
  • server: http 服務上支持若干虛擬主機。每個虛擬主機一個對應的 server 配置項,配置項裏面包含該虛擬主機相關的配置。在提供 mail 服務的代理時,也可以建立若干 server,每個 server 通過監聽的地址來區分。
  • location: http 服務中,某些特定的URL對應的一系列配置項。
  • mail: 實現 email 相關的 SMTP/IMAP/POP3 代理時,共享的一些配置項(因爲可能實現多個代理,工作在多個監聽地址上)。
    user  nobody;
    worker_processes  1;
    error_log  logs/error.log  info;

    events {
        worker_connections  1024;
    }

    http {  
        server {  
            listen          80;  
            server_name     www.linuxidc.com;  
            access_log      logs/linuxidc.access.log main;  
            location / {  
                index index.html;  
                root  /var/www/linuxidc.com/htdocs;  
            }  
        }  

        server {  
            listen          80;  
            server_name     www.Androidj.com;  
            access_log      logs/androidj.access.log main;  
            location / {  
                index index.html;  
                root  /var/www/androidj.com/htdocs;  
            }  
        }  
    }

    mail {
        auth_http  127.0.0.1:80/auth.php;
        pop3_capabilities  "TOP"  "USER";
        imap_capabilities  "IMAP4rev1"  "UIDPLUS";

        server {
            listen     110;
            protocol   pop3;
            proxy      on;
        }
        server {
            listen      25;
            protocol    smtp;
            proxy       on;
            smtp_auth   login plain;
            xclient     off;
        }
    }

在這個配置中,上面提到個五種配置指令上下文都存在。

存在於 main 上下文中的配置指令如下:

  • user
  • worker_processes
  • error_log
  • events
  • http
  • mail
    存在於 http 上下文中的指令如下:

server
存在於 mail 上下文中的指令如下:

  • server

  • auth_http

  • imap_capabilities
    存在於 server 上下文中的配置指令如下:

  • listen

  • server_name

  • access_log

  • location

  • protocol

  • proxy

  • smtp_auth

  • xclient
    存在於 location 上下文中的指令如下:

  • index

  • root

nignx 的請求處理

Nginx 的請求處理
Nginx 使用一個多進程模型來對外提供服務,其中一個 master 進程,多個 worker 進程。master 進程負責管理 Nginx 本身和其他 worker 進程。

所有實際上的業務處理邏輯都在 worker 進程。worker 進程中有一個函數,執行無限循環,不斷處理收到的來自客戶端的請求,並進行處理,直到整個 Nginx 服務被停止。

worker 進程中,ngx_worker_process_cycle()函數就是這個無限循環的處理函數。在這個函數中,一個請求的簡單處理流程如下:

操作系統提供的機制(例如 epoll, kqueue 等)產生相關的事件。
接收和處理這些事件,如是接受到數據,則產生更高層的 request 對象。
處理 request 的 header 和 body。
產生響應,併發送回客戶端。
完成 request 的處理。
重新初始化定時器及其他事件。
請求的處理流程
爲了讓大家更好的瞭解 Nginx 中請求處理過程,我們以 HTTP Request 爲例,來做一下詳細地說明。

從 Nginx 的內部來看,一個 HTTP Request 的處理過程涉及到以下幾個階段。

初始化 HTTP Request(讀取來自客戶端的數據,生成 HTTP Request 對象,該對象含有該請求所有的信息)。
處理請求頭。
處理請求體。
如果有的話,調用與此請求(URL 或者 Location)關聯的 handler。
依次調用各 phase handler 進行處理。
在這裏,我們需要了解一下 phase handler 這個概念。phase 字面的意思,就是階段。所以 phase handlers 也就好理解了,就是包含若干個處理階段的一些 handler。

在每一個階段,包含有若干個 handler,再處理到某個階段的時候,依次調用該階段的 handler 對 HTTP Request 進行處理。

通常情況下,一個 phase handler 對這個 request 進行處理,併產生一些輸出。通常 phase handler 是與定義在配置文件中的某個 location 相關聯的。

一個 phase handler 通常執行以下幾項任務:

獲取 location 配置。
產生適當的響應。
發送 response header。
發送 response body。
當 Nginx 讀取到一個 HTTP Request 的 header 的時候,Nginx 首先查找與這個請求關聯的虛擬主機的配置。如果找到了這個虛擬主機的配置,那麼通常情況下,這個 HTTP Request 將會經過以下幾個階段的處理(phase handlers):

NGX_HTTP_POST_READ_PHASE: 讀取請求內容階段
NGX_HTTP_SERVER_REWRITE_PHASE: Server 請求地址重寫階段
NGX_HTTP_FIND_CONFIG_PHASE: 配置查找階段:
NGX_HTTP_REWRITE_PHASE: Location請求地址重寫階段
NGX_HTTP_POST_REWRITE_PHASE: 請求地址重寫提交階段
NGX_HTTP_PREACCESS_PHASE: 訪問權限檢查準備階段
NGX_HTTP_ACCESS_PHASE: 訪問權限檢查階段
NGX_HTTP_POST_ACCESS_PHASE: 訪問權限檢查提交階段
NGX_HTTP_TRY_FILES_PHASE: 配置項 try_files 處理階段
NGX_HTTP_CONTENT_PHASE: 內容產生階段
NGX_HTTP_LOG_PHASE: 日誌模塊處理階段
在內容產生階段,爲了給一個 request 產生正確的響應,Nginx 必須把這個 request 交給一個合適的 content handler 去處理。如果這個 request 對應的 location 在配置文件中被明確指定了一個 content handler,那麼Nginx 就可以通過對 location 的匹配,直接找到這個對應的 handler,並把這個 request 交給這個 content handler 去處理。這樣的配置指令包括像,perl,flv,proxy_pass,mp4等。

如果一個 request 對應的 location 並沒有直接有配置的 content handler,那麼 Nginx 依次嘗試:

如果一個 location 裏面有配置 random_index on,那麼隨機選擇一個文件,發送給客戶端。
如果一個 location 裏面有配置 index 指令,那麼發送 index 指令指明的文件,給客戶端。
如果一個 location 裏面有配置 autoindex on,那麼就發送請求地址對應的服務端路徑下的文件列表給客戶端。
如果這個 request 對應的 location 上有設置 gzip_static on,那麼就查找是否有對應的.gz文件存在,有的話,就發送這個給客戶端(客戶端支持 gzip 的情況下)。
請求的 URI 如果對應一個靜態文件,static module 就發送靜態文件的內容到客戶端。
內容產生階段完成以後,生成的輸出會被傳遞到 filter 模塊去進行處理。filter 模塊也是與 location 相關的。所有的 fiter 模塊都被組織成一條鏈。輸出會依次穿越所有的 filter,直到有一個 filter 模塊的返回值表明已經處理完成。

這裏列舉幾個常見的 filter 模塊,例如:

server-side includes。
XSLT filtering。
圖像縮放之類的。
gzip 壓縮。
在所有的 filter 中,有幾個 filter 模塊需要關注一下。按照調用的順序依次說明如下:

write: 寫輸出到客戶端,實際上是寫到連接對應的 socket 上。
postpone: 這個 filter 是負責 subrequest 的,也就是子請求的。
copy: 將一些需要複製的 buf(文件或者內存)重新複製一份然後交給剩餘的 body filter 處理。

nginx 配置文件nginx.conf中文詳解

######Nginx配置文件nginx.conf中文詳解#####

#定義Nginx運行的用戶和用戶組
user www www;

#nginx進程數,建議設置爲等於CPU總核心數。
worker_processes 8;
 
#全局錯誤日誌定義類型,[ debug | info | notice | warn | error | crit ]
error_log /usr/local/nginx/logs/error.log info;

#進程pid文件
pid /usr/local/nginx/logs/nginx.pid;

#指定進程可以打開的最大描述符:數目
#工作模式與連接數上限
#這個指令是指當一個nginx進程打開的最多文件描述符數目,理論值應該是最多打開文件數(ulimit -n)與nginx進程數相除,但是nginx分配請求並不是那麼均勻,所以最好與ulimit -n 的值保持一致。
#現在在linux 2.6內核下開啓文件打開數爲65535,worker_rlimit_nofile就相應應該填寫65535。
#這是因爲nginx調度時分配請求到進程並不是那麼的均衡,所以假如填寫10240,總併發量達到3-4萬時就有進程可能超過10240了,這時會返回502錯誤。
worker_rlimit_nofile 65535;


events
{
    #參考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型
    #是Linux 2.6以上版本內核中的高性能網絡I/O模型,linux建議epoll,如果跑在FreeBSD上面,就用kqueue模型。
    #補充說明:
    #與apache相類,nginx針對不同的操作系統,有不同的事件模型
    #A)標準事件模型
    #Select、poll屬於標準事件模型,如果當前系統不存在更有效的方法,nginx會選擇select或poll
    #B)高效事件模型
    #Kqueue:使用於FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 和 MacOS X.使用雙處理器的MacOS X系統使用kqueue可能會造成內核崩潰。
    #Epoll:使用於Linux內核2.6版本及以後的系統。
    #/dev/poll:使用於Solaris 7 11/99+,HP/UX 11.22+ (eventport),IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+。
    #Eventport:使用於Solaris 10。 爲了防止出現內核崩潰的問題, 有必要安裝安全補丁。
    use epoll;

    #單個進程最大連接數(最大連接數=連接數*進程數)
    #根據硬件調整,和前面工作進程配合起來用,儘量大,但是別把cpu跑到100%就行。每個進程允許的最多連接數,理論上每臺nginx服務器的最大連接數爲。
    worker_connections 65535;

    #keepalive超時時間。
    keepalive_timeout 60;

    #客戶端請求頭部的緩衝區大小。這個可以根據你的系統分頁大小來設置,一般一個請求頭的大小不會超過1k,不過由於一般系統分頁都要大於1k,所以這裏設置爲分頁大小。
    #分頁大小可以用命令getconf PAGESIZE 取得。
    #[root@web001 ~]# getconf PAGESIZE
    #4096
    #但也有client_header_buffer_size超過4k的情況,但是client_header_buffer_size該值必須設置爲“系統分頁大小”的整倍數。
    client_header_buffer_size 4k;

    #這個將爲打開文件指定緩存,默認是沒有啓用的,max指定緩存數量,建議和打開文件數一致,inactive是指經過多長時間文件沒被請求後刪除緩存。
    open_file_cache max=65535 inactive=60s;

    #這個是指多長時間檢查一次緩存的有效信息。
    #語法:open_file_cache_valid time 默認值:open_file_cache_valid 60 使用字段:http, server, location 這個指令指定了何時需要檢查open_file_cache中緩存項目的有效信息.
    open_file_cache_valid 80s;

    #open_file_cache指令中的inactive參數時間內文件的最少使用次數,如果超過這個數字,文件描述符一直是在緩存中打開的,如上例,如果有一個文件在inactive時間內一次沒被使用,它將被移除。
    #語法:open_file_cache_min_uses number 默認值:open_file_cache_min_uses 1 使用字段:http, server, location  這個指令指定了在open_file_cache指令無效的參數中一定的時間範圍內可以使用的最小文件數,如果使用更大的值,文件描述符在cache中總是打開狀態.
    open_file_cache_min_uses 1;
    
    #語法:open_file_cache_errors on | off 默認值:open_file_cache_errors off 使用字段:http, server, location 這個指令指定是否在搜索一個文件時記錄cache錯誤.
    open_file_cache_errors on;
}
 
 
 
#設定http服務器,利用它的反向代理功能提供負載均衡支持
http
{
    #文件擴展名與文件類型映射表
    include mime.types;

    #默認文件類型
    default_type application/octet-stream;

    #默認編碼
    #charset utf-8;

    #服務器名字的hash表大小
    #保存服務器名字的hash表是由指令server_names_hash_max_size 和server_names_hash_bucket_size所控制的。參數hash bucket size總是等於hash表的大小,並且是一路處理器緩存大小的倍數。在減少了在內存中的存取次數後,使在處理器中加速查找hash表鍵值成爲可能。如果hash bucket size等於一路處理器緩存的大小,那麼在查找鍵的時候,最壞的情況下在內存中查找的次數爲2。第一次是確定存儲單元的地址,第二次是在存儲單元中查找鍵 值。因此,如果Nginx給出需要增大hash max size 或 hash bucket size的提示,那麼首要的是增大前一個參數的大小.
    server_names_hash_bucket_size 128;

    #客戶端請求頭部的緩衝區大小。這個可以根據你的系統分頁大小來設置,一般一個請求的頭部大小不會超過1k,不過由於一般系統分頁都要大於1k,所以這裏設置爲分頁大小。分頁大小可以用命令getconf PAGESIZE取得。
    client_header_buffer_size 32k;

    #客戶請求頭緩衝大小。nginx默認會用client_header_buffer_size這個buffer來讀取header值,如果header過大,它會使用large_client_header_buffers來讀取。
    large_client_header_buffers 4 64k;

    #設定通過nginx上傳文件的大小
    client_max_body_size 8m;

    #開啓高效文件傳輸模式,sendfile指令指定nginx是否調用sendfile函數來輸出文件,對於普通應用設爲 on,如果用來進行下載等應用磁盤IO重負載應用,可設置爲off,以平衡磁盤與網絡I/O處理速度,降低系統的負載。注意:如果圖片顯示不正常把這個改成off。
    #sendfile指令指定 nginx 是否調用sendfile 函數(zero copy 方式)來輸出文件,對於普通應用,必須設爲on。如果用來進行下載等應用磁盤IO重負載應用,可設置爲off,以平衡磁盤與網絡IO處理速度,降低系統uptime。
    sendfile on;

    #開啓目錄列表訪問,合適下載服務器,默認關閉。
    autoindex on;

    #此選項允許或禁止使用socke的TCP_CORK的選項,此選項僅在使用sendfile的時候使用
    tcp_nopush on;
     
    tcp_nodelay on;

    #長連接超時時間,單位是秒
    keepalive_timeout 120;

    #FastCGI相關參數是爲了改善網站的性能:減少資源佔用,提高訪問速度。下面參數看字面意思都能理解。
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 128k;

    #gzip模塊設置
    gzip on; #開啓gzip壓縮輸出
    gzip_min_length 1k;    #最小壓縮文件大小
    gzip_buffers 4 16k;    #壓縮緩衝區
    gzip_http_version 1.0;    #壓縮版本(默認1.1,前端如果是squid2.5請使用1.0)
    gzip_comp_level 2;    #壓縮等級
    gzip_types text/plain application/x-javascript text/css application/xml;    #壓縮類型,默認就已經包含textml,所以下面就不用再寫了,寫上去也不會有問題,但是會有一個warn。
    gzip_vary on;

    #開啓限制IP連接數的時候需要使用
    #limit_zone crawler $binary_remote_addr 10m;



    #負載均衡配置
    upstream jh.w3cschool.cn {
     
        #upstream的負載均衡,weight是權重,可以根據機器配置定義權重。weigth參數表示權值,權值越高被分配到的機率越大。
        server 192.168.80.121:80 weight=3;
        server 192.168.80.122:80 weight=2;
        server 192.168.80.123:80 weight=3;

        #nginx的upstream目前支持4種方式的分配
        #1、輪詢(默認)
        #每個請求按時間順序逐一分配到不同的後端服務器,如果後端服務器down掉,能自動剔除。
        #2、weight
        #指定輪詢機率,weight和訪問比率成正比,用於後端服務器性能不均的情況。
        #例如:
        #upstream bakend {
        #    server 192.168.0.14 weight=10;
        #    server 192.168.0.15 weight=10;
        #}
        #2、ip_hash
        #每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個後端服務器,可以解決session的問題。
        #例如:
        #upstream bakend {
        #    ip_hash;
        #    server 192.168.0.14:88;
        #    server 192.168.0.15:80;
        #}
        #3、fair(第三方)
        #按後端服務器的響應時間來分配請求,響應時間短的優先分配。
        #upstream backend {
        #    server server1;
        #    server server2;
        #    fair;
        #}
        #4、url_hash(第三方)
        #按訪問url的hash結果來分配請求,使每個url定向到同一個後端服務器,後端服務器爲緩存時比較有效。
        #例:在upstream中加入hash語句,server語句中不能寫入weight等其他的參數,hash_method是使用的hash算法
        #upstream backend {
        #    server squid1:3128;
        #    server squid2:3128;
        #    hash $request_uri;
        #    hash_method crc32;
        #}

        #tips:
        #upstream bakend{#定義負載均衡設備的Ip及設備狀態}{
        #    ip_hash;
        #    server 127.0.0.1:9090 down;
        #    server 127.0.0.1:8080 weight=2;
        #    server 127.0.0.1:6060;
        #    server 127.0.0.1:7070 backup;
        #}
        #在需要使用負載均衡的server中增加 proxy_pass http://bakend/;

        #每個設備的狀態設置爲:
        #1.down表示單前的server暫時不參與負載
        #2.weight爲weight越大,負載的權重就越大。
        #3.max_fails:允許請求失敗的次數默認爲1.當超過最大次數時,返回proxy_next_upstream模塊定義的錯誤
        #4.fail_timeout:max_fails次失敗後,暫停的時間。
        #5.backup: 其它所有的非backup機器down或者忙的時候,請求backup機器。所以這臺機器壓力會最輕。

        #nginx支持同時設置多組的負載均衡,用來給不用的server來使用。
        #client_body_in_file_only設置爲On 可以講client post過來的數據記錄到文件中用來做debug
        #client_body_temp_path設置記錄文件的目錄 可以設置最多3層目錄
        #location對URL進行匹配.可以進行重定向或者進行新的代理 負載均衡
    }
     
     
     
    #虛擬主機的配置
    server
    {
        #監聽端口
        listen 80;

        #域名可以有多個,用空格隔開
        server_name www.w3cschool.cn w3cschool.cn;
        index index.html index.htm index.php;
        root /data/www/w3cschool;

        #對******進行負載均衡
        location ~ .*.(php|php5)?$
        {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            include fastcgi.conf;
        }
         
        #圖片緩存時間設置
        location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$
        {
            expires 10d;
        }
         
        #JS和CSS緩存時間設置
        location ~ .*.(js|css)?$
        {
            expires 1h;
        }
         
        #日誌格式設定
        #$remote_addr與$http_x_forwarded_for用以記錄客戶端的ip地址;
        #$remote_user:用來記錄客戶端用戶名稱;
        #$time_local: 用來記錄訪問時間與時區;
        #$request: 用來記錄請求的url與http協議;
        #$status: 用來記錄請求狀態;成功是200,
        #$body_bytes_sent :記錄發送給客戶端文件主體內容大小;
        #$http_referer:用來記錄從那個頁面鏈接訪問過來的;
        #$http_user_agent:記錄客戶瀏覽器的相關信息;
        #通常web服務器放在反向代理的後面,這樣就不能獲取到客戶的IP地址了,通過$remote_add拿到的IP地址是反向代理服務器的iP地址。反向代理服務器在轉發請求的http頭信息中,可以增加x_forwarded_for信息,用以記錄原有客戶端的IP地址和原來客戶端的請求的服務器地址。
        log_format access '$remote_addr - $remote_user [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" '
        '"$http_user_agent" $http_x_forwarded_for';
         
        #定義本虛擬主機的訪問日誌
        access_log  /usr/local/nginx/logs/host.access.log  main;
        access_log  /usr/local/nginx/logs/host.access.404.log  log404;
         
        #對 "/" 啓用反向代理
        location / {
            proxy_pass http://127.0.0.1:88;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
             
            #後端的Web服務器可以通過X-Forwarded-For獲取用戶真實IP
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             
            #以下是一些反向代理的配置,可選。
            proxy_set_header Host $host;

            #允許客戶端請求的最大單文件字節數
            client_max_body_size 10m;

            #緩衝區代理緩衝用戶端請求的最大字節數,
            #如果把它設置爲比較大的數值,例如256k,那麼,無論使用firefox還是IE瀏覽器,來提交任意小於256k的圖片,都很正常。如果註釋該指令,使用默認的client_body_buffer_size設置,也就是操作系統頁面大小的兩倍,8k或者16k,問題就出現了。
            #無論使用firefox4.0還是IE8.0,提交一個比較大,200k左右的圖片,都返回500 Internal Server Error錯誤
            client_body_buffer_size 128k;

            #表示使nginx阻止HTTP應答代碼爲400或者更高的應答。
            proxy_intercept_errors on;

            #後端服務器連接的超時時間_發起握手等候響應超時時間
            #nginx跟後端服務器連接超時時間(代理連接超時)
            proxy_connect_timeout 90;

            #後端服務器數據回傳時間(代理髮送超時)
            #後端服務器數據回傳時間_就是在規定時間之內後端服務器必須傳完所有的數據
            proxy_send_timeout 90;

            #連接成功後,後端服務器響應時間(代理接收超時)
            #連接成功後_等候後端服務器響應時間_其實已經進入後端的排隊之中等候處理(也可以說是後端服務器處理請求的時間)
            proxy_read_timeout 90;

            #設置代理服務器(nginx)保存用戶頭信息的緩衝區大小
            #設置從被代理服務器讀取的第一部分應答的緩衝區大小,通常情況下這部分應答中包含一個小的應答頭,默認情況下這個值的大小爲指令proxy_buffers中指定的一個緩衝區的大小,不過可以將其設置爲更小
            proxy_buffer_size 4k;

            #proxy_buffers緩衝區,網頁平均在32k以下的設置
            #設置用於讀取應答(來自被代理服務器)的緩衝區數目和大小,默認情況也爲分頁大小,根據操作系統的不同可能是4k或者8k
            proxy_buffers 4 32k;

            #高負荷下緩衝大小(proxy_buffers*2)
            proxy_busy_buffers_size 64k;

            #設置在寫入proxy_temp_path時數據的大小,預防一個工作進程在傳遞文件時阻塞太長
            #設定緩存文件夾大小,大於這個值,將從upstream服務器傳
            proxy_temp_file_write_size 64k;
        }
         
         
        #設定查看Nginx狀態的地址
        location /NginxStatus {
            stub_status on;
            access_log on;
            auth_basic "NginxStatus";
            auth_basic_user_file confpasswd;
            #htpasswd文件的內容可以用apache提供的htpasswd工具來產生。
        }
         
        #本地動靜分離反向代理配置
        #所有jsp的頁面均交由tomcat或resin處理
        location ~ .(jsp|jspx|do)?$ {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:8080;
        }
         
        #所有靜態文件由nginx直接讀取不經過tomcat或resin
        location ~ .*.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|
        pdf|xls|mp3|wma)$
        {
            expires 15d; 
        }
         
        location ~ .*.(js|css)?$
        {
            expires 1h;
        }
    }
}
######Nginx配置文件nginx.conf中文詳解#####

上述例子 中 location ~ .(jsp|jspx|do)?$是匹配以相關文件類型然後單獨處理

匹配規則

location = / {
    [ configuration A ]
}

location / {
    [ configuration B ]
}

location /documents/ {
    [ configuration C ]
}

location ^~ /images/ {
    [ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
    [ configuration E ]
}

  • =表示精確匹配。只有請求的url路徑與後面的字符串完全相等時,纔會命中(優先級最高)。
  • ^~表示如果該符號後面的字符是最佳匹配,採用該規則,不再進行後續的查找。
  • ~表示該規則是使用正則定義的,區分大小寫。
  • ~*表示該規則是使用正則定義的,不區分大小寫。

nginx 負載均衡

幾種常見策略

  • 輪詢(默認)請求過來後,Nginx 隨機分配流量到任一服務器
upstream backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

  • weight=number 設置服務器的權重,默認爲1,權重大的會被優先分配
upstream backend {
    server 127.0.0.1:3000 weight=2;
    server 127.0.0.1:3001 weight=1;
}
  • backup 標記爲備份服務器。當主服務器不可用時,將傳遞與備份服務器的連接。
upstream backend {
    server 127.0.0.1:3000 backup;
    server 127.0.0.1:3001;
}
  • ip_hash 保持會話,保證同一客戶端始終訪問一臺服務器。
upstream backend {
    ip_hash;  
    server 127.0.0.1:3000 backup;
    server 127.0.0.1:3001;
}
  • least_conn 優先分配最少連接數的服務器,避免服務器超載請求過多。
upstream backend {
    least_conn;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

當我們需要代理一個集羣時候可以通過下面這種方式實現

http {

    upstream backend {
        server 127.0.0.1:3000;
        server 127.0.0.1:3001;
    }

    ...
    server {
        listen      9000;
        server_name localhost;
        
        location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Scheme $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  # 真實ip地址
            # backend 是 反向代理upstream 下的名稱
            proxy_pass backend; 
        }
    }
}

配置目錄下 niginx -t 檢查nignix 配置是否正確
nginx -s reload 重新加載配置 立即生效

反向代理【proxy_pass】

所謂反向代理,很簡單,其實就是在location這一段配置中的root替換成proxy_pass即可。root說明是靜態資源,可以由Nginx進行返回;而proxy_pass說明是動態請求,需要進行轉發,比如代理到Django上。

反向代理,上面已經說了,過程是透明的,比如說request -> Nginx -> Django,那麼對於Django而言,請求的IP地址就是Nginx的地址,而非真實的request地址,這一點需要注意。不過好在Nginx不僅僅可以反向代理請求,還可以由用戶自定義設置HTTP HEADER。

參考引用

Nginx 入門指南
前端想要了解的Nginx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章