深入理解Nginx及使用Nginx實(shí)現(xiàn)負(fù)載均衡
前言
最近在部署項(xiàng)目時(shí)要求實(shí)現(xiàn)負(fù)載均衡,有趣的是發(fā)現(xiàn)網(wǎng)上一搜全部都是以下類似的配置文件
- upstream localhost{
- server 127.0.0.1:8080 weight=1;
- server 127.0.0.1:8081 weight=1;
- }
- server {
- listen 80;
- server_name localhost;
- location / {
- proxy_pass http://localhost;
- index index.html index.htm index.jsp;
- }
- }
所以打算來看看Nginx內(nèi)部原理,這篇博客主要介紹Nginx如何實(shí)現(xiàn)反向代理以及在Nginx中負(fù)載均衡的參數(shù)使用
一、正向代理與反向代理
正向代理是代理客戶端,也就是客戶端能真正接觸到的,比如訪問外網(wǎng)時(shí)需要使用VPN軟件,在這個(gè)軟件中用戶可以選擇連接哪里的服務(wù)器。
反向代理則是代理服務(wù)端,用戶感知不到,只是客戶端把請(qǐng)求發(fā)到服務(wù)端的端口時(shí),Nginx監(jiān)聽到了便把該端口的請(qǐng)求轉(zhuǎn)發(fā)到不同的服務(wù)器上。就以上面配置文件來講解,當(dāng)在網(wǎng)址中輸入http://localhost:80/時(shí)(不加80一樣時(shí)默認(rèn)進(jìn)入80端口,這里為了表示清楚),而后Nginx監(jiān)聽到80端口的請(qǐng)求之后,就會(huì)查找對(duì)應(yīng)的location來執(zhí)行。由上面的配置文件我們可以看出是將請(qǐng)求轉(zhuǎn)發(fā)到了不同的端口。這是在服務(wù)器中執(zhí)行的,用戶不可見。
而服務(wù)端中我們最常使用的反向代理的工具就是Nginx。
二、Nginx內(nèi)部基本架構(gòu)
nginx在啟動(dòng)后以daemon的方式在后臺(tái)運(yùn)行,會(huì)有一個(gè)master進(jìn)程和多個(gè)worker進(jìn)程。
master進(jìn)程:主要用來管理worker進(jìn)程,包含:接收來自外界的信號(hào),向各worker進(jìn)程發(fā)送信號(hào),監(jiān)控worker進(jìn)程的運(yùn)行狀態(tài),當(dāng)worker進(jìn)程退出后(異常情況下),會(huì)自動(dòng)重新啟動(dòng)新的worker進(jìn)程。
worker進(jìn)程:處理基本的網(wǎng)絡(luò)事件了。多個(gè)worker進(jìn)程之間是對(duì)等的,他們同等競爭來自客戶端的請(qǐng)求,各進(jìn)程互相之間是獨(dú)立的。一個(gè)請(qǐng)求,只可能在一個(gè)worker進(jìn)程中處理,一個(gè)worker進(jìn)程,不可能處理其它進(jìn)程的請(qǐng)求。worker進(jìn)程的個(gè)數(shù)是可以設(shè)置的,一般我們會(huì)設(shè)置與機(jī)器cpu核數(shù)一致,或者直接設(shè)置參數(shù)worker_processes auto;

所以Nginx基本的架構(gòu)就如下:

當(dāng)我們輸入./nginx -s reload,就是來重啟nginx,./nginx -s stop,就是來停止nginx的運(yùn)行,這里面是如何做到的?執(zhí)行命令時(shí),我們是啟動(dòng)一個(gè)新的nginx進(jìn)程,而新的nginx進(jìn)程在解析到reload參數(shù)后,就知道我們的目的是控制nginx來重新加載配置文件了,它會(huì)向master進(jìn)程發(fā)送信號(hào)。master進(jìn)程在接到信號(hào)后,會(huì)先重新加載配置文件,然后再啟動(dòng)新的worker進(jìn)程,并向所有老的worker進(jìn)程發(fā)送信號(hào),告訴他們可以光榮退休了。新的worker在啟動(dòng)后,就開始接收新的請(qǐng)求,而老的worker在收到來自master的信號(hào)后,就不再接收新的請(qǐng)求,并且在當(dāng)前進(jìn)程中的所有未處理完的請(qǐng)求處理完成后,再退出。所以使用上面命令重啟Nginx的時(shí)候服務(wù)是不中斷的。
三、Nginx如何處理客戶端請(qǐng)求
首先來解釋一下上面的架構(gòu)圖:每個(gè)worker進(jìn)程都是從master進(jìn)程分支過來的,在master進(jìn)程里面,先建立好需要監(jiān)聽的socket之后,然后再分支出多個(gè)worker進(jìn)程。所有worker進(jìn)程的listenfd(socket中l(wèi)istenfd是指客戶端連接本機(jī)時(shí)的fd,是用來和客戶端通信用的)會(huì)在新連接到來時(shí)變得可讀,為保證只有一個(gè)進(jìn)程處理該連接,所有worker進(jìn)程在注冊(cè)listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個(gè)進(jìn)程注冊(cè)listenfd讀事件,在讀事件里調(diào)用accept接受該連接。
在Nginx中worker進(jìn)程之間是平等的,每個(gè)進(jìn)程,處理請(qǐng)求的機(jī)會(huì)也是一樣的。當(dāng)Nginx監(jiān)聽80端口時(shí),一個(gè)客戶端的連接請(qǐng)求過來,每個(gè)進(jìn)程都有可能處理這個(gè)連接,上面說到是每個(gè)worker進(jìn)程都會(huì)去搶注listenfd讀事件。當(dāng)一個(gè)worker進(jìn)程在accept這個(gè)連接之后,就開始讀取請(qǐng)求,解析請(qǐng)求,處理請(qǐng)求,產(chǎn)生數(shù)據(jù)后,再返回給客戶端,最后才斷開連接,這樣一個(gè)完整的請(qǐng)求就是這樣的了。這里需要注意的是一個(gè)請(qǐng)求,完全由worker進(jìn)程來處理,而且只在一個(gè)worker進(jìn)程中處理。
下面兩幅流程圖能很好的幫我們理解


四、Nginx如何處理事件并且實(shí)現(xiàn)高并發(fā)
Nginx內(nèi)部采用了異步非阻塞的方式來處理請(qǐng)求,也就是說,Nginx是可以同時(shí)處理成千上萬個(gè)請(qǐng)求的。
異步非阻塞:當(dāng)一個(gè)網(wǎng)絡(luò)請(qǐng)求過來時(shí),我們并不依賴于這個(gè)請(qǐng)求才能做后續(xù)操作,那么這個(gè)請(qǐng)求就是異步操作,也就是調(diào)用者在沒有得到結(jié)果之前同樣可以執(zhí)行后續(xù)的操作。非阻塞就是當(dāng)前進(jìn)程/線程沒有得到請(qǐng)求調(diào)用的結(jié)果時(shí)也不會(huì)妨礙到進(jìn)程/線程后續(xù)的操作??梢钥闯霎惒胶头亲枞膶?duì)象是不同的。
五、Nginx負(fù)載均衡的算法及參數(shù)
round robin(默認(rèn)):輪詢方式,依次將請(qǐng)求分配到后臺(tái)各個(gè)服務(wù)器中,適用于后臺(tái)機(jī)器性能一致的情況,若服務(wù)器掛掉,可以自動(dòng)從服務(wù)列表中剔除
weight:根據(jù)權(quán)重來分發(fā)請(qǐng)求到不同服務(wù)器中,可以理解為比例分發(fā),性能較高服務(wù)器分多點(diǎn)請(qǐng)求,較低的則分少點(diǎn)請(qǐng)求
IP_hash:根據(jù)請(qǐng)求者ip的hash值將請(qǐng)求發(fā)送到后臺(tái)服務(wù)器中,保證來自同一ip的請(qǐng)求被轉(zhuǎn)發(fā)到固定的服務(wù)器上,解決session問題
- upstream localhost {
- ip_hash;
- server 127.0.0.1:8080;
- server 127.0.0.1:8080;
- }
上面是最基本的三種算法,我們還可以通過改變參數(shù)來自行配置負(fù)載均衡
- 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;
- }
參數(shù)列表如下:
