漸進深入理解Nginx

逆月翎發表於2019-09-21

文章原創於公眾號:程式猿周先森。本平臺不定時更新,喜歡我的文章,歡迎關注我的微信公眾號。file

之前其實寫過一篇文章具體介紹過:最基礎的Nginx教學,當時有提到過Nginx有一個重要的功能:負載均衡。所以這篇文章主要講講Nginx如何實現反向代理以及在Nginx中負載均衡的引數使用。

一、代理

正向代理

正向代理也是大家最常接觸的到的代理模式,那究竟什麼是正向代理呢?我們都知道Google在國內是無法正常訪問的,但是某些時候我們由於技術問題需要去訪問Google時,我們會先找到一個可以訪問Google的代理伺服器,我們將請求傳送到代理伺服器,代理伺服器去訪問Google,然後將訪問到的資料返回給我們,這樣的過程就是正向代理。

正向代理的特點

正向代理最大的特點是客戶端需要明確知道要訪問的伺服器地址,Google伺服器只清楚請求來自哪個代理伺服器,而不清楚來自哪個具體的客戶端,正向代理可以隱藏真實客戶端的具體資訊。

file

客戶端必須設定正向代理伺服器,而且需要知道正向代理伺服器的IP地址以及代理程式的埠。一句話來概括就是正向代理代理的是客戶端,是一個位於客戶端和Google伺服器之間的伺服器,為了從Google伺服器取得資料,客戶端向代理伺服器傳送一個請求並指定目標(Google伺服器),然後代理向原始伺服器轉交請求並將獲得的資料返回給客戶端。

正向代理的使用:

  • 訪問國外無法訪問的網站
  • 做快取,加速訪問資源
  • 對客戶端訪問授權,上網進行認證
  • 代理可以記錄使用者訪問記錄(上網行為管理),對外隱藏使用者資訊

反向代理

說完了什麼是正向代理,我們接下來看看什麼叫做反向代理,如果我們網站每日訪問量達到某個上限,單個伺服器遠遠不能符合我們日常需求,這時候我們首先會想到分散式部署。通過部署多臺伺服器來解決訪問人數限制的問題,然後我們功能其實大部分都是通過Nginx反向代理來實現的。我們可以看下圖:file

反向代理的特點

我們可以清楚的看到,多個客戶端給伺服器傳送的請求,Nginx伺服器接收到請求以後,按照一定的規則轉發到不同的伺服器進行業務邏輯處理。此時請求來源於哪個客戶端是確定的,但是請求由哪臺伺服器處理的並不明確,Nginx扮演的就是一個反向代理角色。可以這樣來理解,反向代理對外都是透明的,訪問者並不知道自己訪問的是一個代理。反向代理代理的是服務端,主要用於伺服器叢集分散式部署的情況下,反向代理隱藏了伺服器的資訊。

反向代理的使用:

  • 保證內網的安全,通常將反向代理作為公網訪問地址,Web伺服器是內網
  • 負載均衡,通過反向代理伺服器來優化網站的負載

正向代理與反向代理區別file

  • 在正向代理中,隱藏了請求來源的客戶端資訊;
  • 在反向代理中,隱藏了請求具體處理的服務端資訊;

服務端中我們最常使用的反向代理的工具就是Nginx。

二、基本架構

Nginx在啟動後以daemon的方式在後臺執行,會有一個master程式和多個worker程式:

  1. Nginx 在啟動後,會有一個 master 程式和多個相互獨立的 worker 程式。
  2. 接收來自外界的訊號,向各worker程式傳送訊號,每個程式都有可能來處理這個連線。
  3. master 程式能監控 worker 程式的執行狀態,當 worker 程式退出後(異常情況下),會自動啟動新的 worker 程式。

master程式:主要用來管理worker程式,包含:

  1. 接收來自外界的訊號
  2. 向各worker程式傳送訊號
  3. 監控worker程式的執行狀態
  4. 當worker程式異常退出後,會自動重新啟動新的worker程式。

worker程式:處理基本的網路事件了。多個worker程式之間是對等的,他們同等競爭來自客戶端的請求,各程式互相之間是獨立的。一個請求只能在一個worker程式中處理,一個worker程式,不可能處理其它程式的請求。worker程式的個數是可以設定的,一般我們會設定與機器cpu核數一致,或者直接設定引數worker_processes auto;

Nginx基本架構如下:file

我們可以輸入nginx -s reload來重啟Nginx,nginx -s stop來停止Nginx的執行,執行這些命令時其實會啟動一個新的Nginx程式,而新的Nginx程式在解析到reload引數後,其實就可以知道使用者執行這個命令是控制Nginx重新載入配置檔案,於是向master程式傳送訊號。master程式接到訊號會先重新載入配置檔案,然後啟動新的worker程式並向所有舊worker程式傳送訊號提示老程式可以停止執行了。新的worker啟動成功後就開始接收新的請求,而舊worker在收到來自master的訊號後停止接收新的請求,在未處理完的請求處理完成後程式就會退出。所以說使用nginx -s reload命令重啟Nginx的時候服務是不中斷的。

三、Nginx處理客戶端請求方式

剛才有講到過每個worker程式都是從master程式分支的,所以在master程式裡面需要先建立好需要監聽的socket然後再分支出多個worker程式。所有worker程式listenfd事件會在新連線時變成可讀,為保證只有一個程式處理該連線,所以需要設定互斥鎖,所有worker程式需要搶互斥鎖,搶到互斥鎖的work程式註冊listenfd讀事件,在listenfd讀事件裡呼叫accept接受該連線。當Nginx監聽80埠時,一個客戶端的連線請求過來的時候,每個worker程式都會去搶互斥鎖註冊listenfd讀事件。當一個worker程式在accept這個連線之後,就開始處理請求獲取資料,再將資料返回給客戶端,然後斷開連線,到這裡一個請求結束。

一個請求,完全由worker程式來處理,而且只在一個worker程式中處理。

我下面貼一個簡單的配置:

server {

listen 80;

server_name aaa.com www.aaa.com;

}

server {

listen 80;

server_name aaa.cn www.aaa.cn;

}

server {

listen 80;

server_name aaa.org www.aaa.org;

}

當接收到客戶端http請求,Nginx根據請求頭的Host欄位決定請求應該由哪一臺伺服器處理,如果Host欄位的值沒有匹配的伺服器或者請求中沒有Host欄位,Nginx會將請求路由至這個埠的預設伺服器。沒有顯示配置預設伺服器,則預設伺服器則為第一個配置。當然我們還可以使用default_server引數指定預設伺服器。

server {listen 80 default_server;server_name aaa.com www.aaa.com;}

這裡需要注意一下:配置預設伺服器是監聽埠號,而不是伺服器名稱。

四、Nginx實現高併發

Nginx內部採用了非同步非阻塞的方式處理請求,使用了epoll和大量的底層程式碼優化。可以同時處理成千上萬個請求的。

非同步非阻塞:每進來一個request,會有一個worker程式去處理。但不是全程的處理,處理到什麼程度呢?處理到可能發生阻塞的地方,比如向後端伺服器轉發request,並等待請求返回。這個處理的worke會在傳送完請求後註冊一個事件:“如果upstream返回了,再進行執行接下來的工作”。此時,如果再有request 進來,他就可以很快再按這種方式處理。而一旦後端伺服器返回了,就會觸發這個事件,worker程式會來接手request接著往下執行。

而Nginx採用一個master程式,多個woker程式的模式。master程式主要負責收集、分發請求。每當一個請求過來時,master就拉起一個worker程式負責處理這個請求。同時master程式也負責監控woker的狀態,保證高可靠性,woker程式一般設定為跟cpu核心數一致。Nginx的woker程式在同一時間可以處理的請求數只受記憶體限制,可以處理多個請求。Nginx 的非同步非阻塞工作方式可以把當中的程式空閒等待時間利用起來,因此表現為少數幾個程式就解決了大量的併發問題。

Nginx中以epoll為例子,當事件沒準備好時,放到epoll裡面,事件準備好了,Nginx就去讀寫,當讀寫返回EAGAIN時,就將它再次加入到epoll裡面。這樣,只要有事件準備好了,Nginx就可以去處理它,只有當所有事件都沒準備好時,才在epoll裡面等著。這樣便實現了所謂的併發處理請求,但是執行緒只有一個,所以同時能處理的請求當然只有一個了,只是在請求間進行不斷地切換而已。

Nginx單執行緒機制與多執行緒相比優勢:

  • 在於不需要建立執行緒。
  • 每個請求佔用的記憶體也很少。
  • 沒有上下文切換。
  • 事件處理非常的輕量級。
  • 併發數再多也不會導致無謂的資源浪費。

五、Nginx負載均衡的演算法及引數

  • weight輪詢(預設):接收到的請求按照請求順序逐一分配到不同的後端伺服器,如果在使用過程中,某一臺伺服器當機,Nginx會自動將該伺服器剔除出佇列,請求受理情況不會受到任何影響。這種方式下,可以給不同的後端伺服器設定一個權重值,權重資料越大,伺服器被分配到請求的機率越大。
  • ip_hash:每個請求按照發起客戶端的ip的hash結果進行匹配,這樣的演算法下一個固定ip地址的客戶端總會訪問到同一個後端伺服器。
  • fair:智慧調整排程演算法,動態的根據後端伺服器的請求響應時間進行均衡分配,響應時間短處理效率高的伺服器分配到請求的概率高,響應時間長處理效率低的伺服器分配到的請求少。
  • url_hash:按照訪問的url的hash結果分配請求,每個請求的url會指向後端固定的某個伺服器,可以在Nginx作為靜態伺服器的情況下提高快取效率。

上面是最基本的4種演算法,我們還可以通過改變引數來自行配置負載均衡:

upstream localhost{

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;

}

  • down表示當前的伺服器停止參與負載。
  • weight預設為1,weight越大,負載的權重就越大。
  • backup表示其它所有的非backup機器down或者忙的時候,請求backup機器。所以這臺機器壓力會最輕。

file

歡迎關注我的個人公眾號:程式猿周先森file

相關文章