Nginx 是什么?Nginx高并發(fā)架構(gòu)拆解指南
你是一個程序員,你在電腦上編輯了一段文本,將它保存為 txt 文件。將它拖到瀏覽器打開,就能看到文件里的內(nèi)容。
但這看起來太過單調(diào),為了讓畫面更豐富,我們定個規(guī)則,在文本邊上加個兩個h1符號,文本就以標(biāo)題形式展示。
加入ul和li就能變成列表,加入img還能讓url文本直接變成對應(yīng)的圖片。
圖片
這些帶尖括號的特殊符號,我們叫它標(biāo)簽。只要瀏覽器識別到這些標(biāo)簽,就展示對應(yīng)的樣式。
為了將這個自帶標(biāo)簽的文本跟 txt 純文本區(qū)分開來,我們給了它新的后綴名, html。
瀏覽器只要識別到文件是 html,就會解析里面的標(biāo)簽,這樣我們就有了標(biāo)題、輸入框等各種豐富的內(nèi)容了。這其實(shí)就是我們平時(shí)在瀏覽器中看到的網(wǎng)頁。
但不同的是,這個 html 文件是瀏覽器從我們本地電腦文件中打開的。
本地瀏覽器打開html
而我們平時(shí)訪問的網(wǎng)頁,則是從某臺遠(yuǎn)端服務(wù)器,將文件傳到我們電腦的瀏覽器后打開的。
從遠(yuǎn)端服務(wù)器得到html打開
那么問題就來了,我們是怎么獲得這個遠(yuǎn)端服務(wù)器上的 html 文件的?
沒有什么是加一層中間層不能解決的,如果有,那就再加一層,這次我們要加的中間層是 Nginx.
nginx中間層
假設(shè)我們完全不了解 nginx,來看下它是怎么設(shè)計(jì)出來的。
HTTP 服務(wù)器是什么?
想要讓本地的瀏覽器,獲取到放在遠(yuǎn)端服務(wù)器上的 html 文件。
那很簡單,我們可以在遠(yuǎn)端服務(wù)器啟動一個進(jìn)程,這個進(jìn)程對外提供 http 服務(wù),說白了就是提供了個 url。
用戶在瀏覽器中輸入這個 url, 回車,瀏覽器就會向 這個進(jìn)程發(fā)起 http 請求,
進(jìn)程收到瀏覽器的請求后,就將 html 文件發(fā)給瀏覽器,瀏覽器完成解析和展示,完美。
而像這種根據(jù)瀏覽器請求,返回 html 文件的服務(wù)進(jìn)程,其實(shí)就叫 http 服務(wù)器。
有了它,前端開發(fā)老哥寫的各種 html 文件就能部署到遠(yuǎn)端服務(wù)器上,對外提供網(wǎng)頁服務(wù)了。
http服務(wù)器是什么
反向代理是什么?
但一個完整產(chǎn)品往往不止有前端頁面,還有后端服務(wù),比如某寶,前端商城頁面需要從后端服務(wù)那獲取最新的商品數(shù)據(jù)。
前后端分離
假設(shè)現(xiàn)在前端頁面已經(jīng)被加載到瀏覽器中,瀏覽器會按頁面里寫好的代碼邏輯,向后端商品服務(wù)發(fā)起請求,獲取數(shù)據(jù),流量小的時(shí)候沒什么問題,流量變大后,后端服務(wù)器扛不住的話,就需要增加商品服務(wù)的個數(shù),服務(wù)變多后,每個都有對應(yīng)的 ip 和端口,瀏覽器就不知道該訪問哪個服務(wù)了。
所以我們還需要在這幾個后端服務(wù)前面加一個進(jìn)程,對外提供一個 url 域名,請求來了,由這個進(jìn)程均勻轉(zhuǎn)發(fā)給背后的幾個服務(wù),讓每個服務(wù)都能處理上請求,也就實(shí)現(xiàn)了所謂的負(fù)載均衡。
像這種,屏蔽掉背后具體有哪些服務(wù)器的代理方式,就是我們常說的反向代理。
反向代理
有了反向代理,我們對外就可以只提供一個url域名,背后根據(jù)需要, 隨時(shí)擴(kuò)縮容服務(wù)。
這個反向代理的功能,正好可以加到前面放 html 文件的進(jìn)程上。
那現(xiàn)在這個進(jìn)程就很靈性了,既可以為 前端 html 文件提供 http 服務(wù)器的功能,當(dāng) html 文件被加載到瀏覽器,并向后端發(fā)起請求的時(shí)候,這個進(jìn)程還能為后端服務(wù)器提供反向代理的功能。
http服務(wù)器+反向代理
模塊化網(wǎng)關(guān)能力
既然是中間層,所有網(wǎng)絡(luò)流量都要經(jīng)過進(jìn)程,那它高低也算個網(wǎng)關(guān)了。
網(wǎng)關(guān)
于是我們就可以順理成章的在它上面加入一些通用網(wǎng)關(guān)能力,比如加個日志,記錄每次調(diào)用的結(jié)果,方便后續(xù)排查問題,又比如加個對輸入輸出的內(nèi)容進(jìn)行壓縮的功能,減小網(wǎng)絡(luò)帶寬消耗,又或者是對某個 IP 進(jìn)行限流或封禁,甚至還可以修改輸入輸出的內(nèi)容。能實(shí)現(xiàn)的功能實(shí)在太多,想象空間很大,于是將這部分功能設(shè)計(jì)為開放接口,讓用戶通過自定義模塊來實(shí)現(xiàn)特定功能。
這還不夠,現(xiàn)在這個網(wǎng)關(guān)只支持http,我們其實(shí)還能擴(kuò)展下,讓它支持tcp,udp,http2和websocket,你能想到的我都要支持,我本來不支持的,自會有人通過自定義模塊幫我支持。
支持多種通用能力和協(xié)議
配置能力
前面提到那么多種能力,用戶肯定不會全用上,所以需要有個地方讓人選擇用哪些能力,于是我們可以加個配置文件,也就是nginx.conf,用戶想用什么能力,就在配置文件上聲明清楚就行,非常方便。
nginx.conf配置
單線程
現(xiàn)在這個網(wǎng)關(guān)進(jìn)程的主要任務(wù)就是跟上下游建立網(wǎng)絡(luò)連接,順便內(nèi)部做下處理。多個客戶端請求通過網(wǎng)絡(luò)進(jìn)入到一個進(jìn)程,如果用多線程并發(fā)處理,那就需要考慮并發(fā)問題,同時(shí)影響性能。怎么辦呢?
多線程
很簡單!外部不管有多少有個網(wǎng)絡(luò)連接,網(wǎng)關(guān)進(jìn)程收到客戶端請求后,都統(tǒng)一塞到一個線程上,在一個線程上處理客戶端請求,什么并發(fā)問題和線程切換開銷,完全不存在!
單線程
多 worker 進(jìn)程
但單個進(jìn)程要單線程處理那么多流量,哪怕再快,壓力也不小,萬一這里面有美羊羊發(fā)的流量,你覬覦那么久?怎么忍心讓她久等?沸羊羊你說話!
怎么辦呢?既然多線程不行,那我們就上多進(jìn)程。
于是可以將單個進(jìn)程改成多個進(jìn)程,我們管它們叫 worker 進(jìn)程。進(jìn)程之間互相獨(dú)立,一個 worker 跪了不影響另外一個 worker 進(jìn)程。
多worker進(jìn)程
讓多個 worker 進(jìn)程同時(shí)監(jiān)聽一個 ip 地址+端口。只要一有流量進(jìn)來,操作系統(tǒng)就會隨機(jī)給到其中一個進(jìn)程處理。將進(jìn)程數(shù)量設(shè)置為跟操作系統(tǒng)cpu核數(shù)一致,那每個進(jìn)程都能得到一個核,開足馬力猛猛干。
worker數(shù)與核數(shù)一致
看到這里,問題就來了,為什么多個進(jìn)程同時(shí)監(jiān)聽一個端口不會出現(xiàn)端口沖突(port is already in use),評論區(qū)告訴我答案。
共享內(nèi)存
但多 worker 進(jìn)程的情況下,同一個客戶端的多個請求會隨機(jī)打到某個 worker ,對于限流這種需要計(jì)數(shù)的場景,就會被分散到多個 worker 上單獨(dú)計(jì)數(shù),那還怎么限流,所以還需要給這些 worker 進(jìn)程 分配一個共享內(nèi)存區(qū)域,方便多個進(jìn)程之間共用同一份數(shù)據(jù)做邏輯,確保系統(tǒng)數(shù)據(jù)一致性。
共享內(nèi)存
proxy cache
作為網(wǎng)關(guān),它在收到前端網(wǎng)頁請求后,會轉(zhuǎn)發(fā)給后端,并將后端處理結(jié)果中轉(zhuǎn)給前端。如果它能將響應(yīng)結(jié)果緩存起來,這樣下次收到同樣的請求,直接將緩存里的數(shù)據(jù)返回給前端,從而減少響應(yīng)時(shí)間和網(wǎng)絡(luò)負(fù)載。
那這個數(shù)據(jù)是放在共享內(nèi)存里嗎?內(nèi)存貴,不合適,我們可以維護(hù)些磁盤文件,用于在前端請求后端的過程中,暫存后端響應(yīng)的結(jié)果,后面再有相同請求,就可以將磁盤里的數(shù)據(jù)返回。
這又是經(jīng)典的空間換時(shí)間,用廉價(jià)的磁盤空間換取網(wǎng)絡(luò)傳輸和cpu計(jì)算耗時(shí)。對于后端響應(yīng)較慢或重復(fù)請求較多的場景,讀寫磁盤總歸比直接將請求打到后端來得快。這些用于緩存響應(yīng)數(shù)據(jù)的磁盤文件,就是 所謂的proxy cache 。
proxy cache
加入 master 進(jìn)程
但這還不夠,現(xiàn)在每個 worker 都會分走一部分流量,如果功能更新,所有 worker 同時(shí)一起重啟,上面的網(wǎng)絡(luò)連接就會全部斷掉。更好的方式是創(chuàng)建 worker 和關(guān)閉 worker 挨個陸續(xù)執(zhí)行,這樣前端網(wǎng)頁連接斷開后還能去連另外一個worker,保證任意時(shí)間一直有worker在工作。也就是所謂的滾動升級。因此還需要一個新的進(jìn)程協(xié)調(diào)各個 worker 誰先誰后,這個協(xié)調(diào)進(jìn)程,就是所謂的 master 進(jìn)程。讓master讀取前面提到的nginx.conf配置,統(tǒng)一管理多個worker。
master進(jìn)程
nginx 是什么
好啦,到這里,當(dāng)初那個簡陋的單進(jìn)程網(wǎng)關(guān)服務(wù),就變成了一個支持動態(tài)配置多種通用網(wǎng)關(guān)能力和多種網(wǎng)絡(luò)協(xié)議,單 master 多 worker 架構(gòu)、多個worker進(jìn)程之間共享內(nèi)存和proxy cache,對外提供一個IP+端口,支持 http 服務(wù)器和反向代理的高性能網(wǎng)關(guān)服務(wù)。
它就是所謂的 nginx。
nginx是什么
它不僅支持日志、限流等各種通用能力、還支持自定義網(wǎng)關(guān)能力,只要你寫好配置,就能讓它給你當(dāng)牛做馬。性能上 5w qps 非常輕松,應(yīng)付你那只有幾十 qps 的服務(wù)更是綽綽有余了。
現(xiàn)在大家通了嗎?好啦,如果你覺得這個視頻對你有幫助,記得點(diǎn)贊并轉(zhuǎn)發(fā)給你那不成器的兄弟,文字版的筆記見評論區(qū)。
最后遺留一個問題,想必大家也發(fā)現(xiàn)了,聊到現(xiàn)在它其實(shí)也只是某臺服務(wù)器上的多個進(jìn)程,一旦服務(wù)器跪了,nginx 也就跪了,存在單點(diǎn)問題。
nginx單點(diǎn)問題
那怎么解決 nginx 的單點(diǎn)問題呢?nginx 有集群模式嗎?評論區(qū)告訴我答案。
最后的最后再遺留一個問題,你聽說過大數(shù)據(jù)嗎?你知道大家是怎么解決大數(shù)據(jù)問題的嗎?