摘要:反向代理反向代理反向代理負載均衡鑒權限流等邏輯架構在邏輯上分為入口層,模塊化的功能處理層,系統調用層。多個共同監聽事件并處理,反向代理會把請求轉發給后端服務。
一.概述
本文將深入剖析nginx的架構。
第一部分介紹nginx現有框架,用典型的4+1視圖闡述,包括邏輯架構,開發架構,運行架構,物理架構,功能用例,nginx為單機服務,不考慮物理架構。其中功能用例概述nginx功能;邏輯架構主要介紹nginx高度模塊化中各個模塊的分層和依賴關系;開發架構主要描述nginx的代碼結構和代碼內容簡介;重點是運行架構,nginx一主多從的進程模型架構和通信,高并發進程和IO并發的選型等。
第二部分對比nginx運行架構和其他開源運行架構,總結nginx為何要這樣選型;介紹nginx邏輯架構中的優點。
本文適合閱讀對象:1)已經看過nginx代碼,本文幫你高度抽象總結了nginx的架構和與我自己設計相比較,nginx哪里設計的優點,試著從架構層來重新看下代碼;2)研究各種系統架構的人,本文從統一的架構視圖介紹,無需知道nginx的代碼細節,列出了與其他架構比,nginx架構的亮點。3)還未看過nginx的代碼,關注第二章,可以看四個視圖忽略對特性的分析和架構的思考,幫助了解nginx有什么功能、如何組織代碼、如何運行。
關鍵詞:Nginx架構,nginx功能,nginx邏輯架構,nginx代碼結構,nginx運行架構,nginx高性能實現 二.nginx現有架構實現 功能介紹nginx最核心的功能是web服務器和反向代理服務器,web服務器完成對 http請求協議的解析 和 以http協議格式響應請求、緩存、日志處理這些 基本web服務器 功能,反向代理服務器完成對請求的轉發、負載均衡、鑒權、限流、緩存、日志處理等代理常用功能。nginx在這方面提供了豐富的功能,包括對http2,ssl等等的支持。除了http外,nginx還支持mail服務和普通的tcp,udp協議的反向代理功能。一下列出了常用功能,詳細所有功能見參考1
### http服務器/反向代理服務器
靜態文件,fastcgi,uwsgi,scgi,memcached 服務
緩存
負載均衡
SSL/TLS
HTTP2
鑒權/限流
虛擬servers
功能用例舉例:web服務器和反向代理服務器的功能(第一個locatiion為服務器,后面是反向代理)。在nginx中配置如圖配置,啟動nginx加載配置后,發起http請求,獲取服務器響應。
server{ listen 8091; root /home/xiaoju/yyl/; index index.php; location / { root html; index index.html index.htm; } location ~ .php$ { rewrite /(.*)$ /index.php/$1 break; fastcgi_index index.php; fastcgi_pass 127.0.0.1:9000; include fastcgi.conf; } }
curl localhost:8091 -v * About to connect() to localhost port 8091 (#0) * Trying 127.0.0.1... connected * Connected to localhost (127.0.0.1) port 8091 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2 > Host: localhost:8091 > Accept: */* > < HTTP/1.1 200 OK < Server: nginx/1.13.11 < Date: Sun, 02 Dec 2018 06:29:01 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 612 < Last-Modified: Tue, 10 Apr 2018 15:32:02 GMT < Connection: keep-alive < ETag: "5accd8f2-264" < Accept-Ranges: bytes <mail反向代理Welcome to nginx! ... * Connection #0 to host localhost left intact * Closing connection #0
mail反向代理
SSL; STARTTLS/STLS
TCP/UDP反向代理 socket,websocketSSL/TLS, 負載均衡, 鑒權/限流等
2.邏輯架構nginx在邏輯上分為入口層,模塊化的功能處理層,系統調用層。入口調用配置模塊和核心模塊,各核心模塊分別調用各自功能模塊,系統調用層封裝了各個操作系統的功能被功能處理層使用。邏輯架構最明顯主要的特征就是高度模塊化,所有功能都是模塊,每個模塊都統一結構,下面先看下這個統一結構,然后分別介紹各個模塊。
nginx除了main等少量代碼,其他全都是模塊,所有模塊都是Ngx_module_t的抽象,只有初始化,退出,對配置項的處理;每個模塊內部也都有自己模塊ngx_xx_module_t的抽象;配置也高度抽象統一的結構ngx_command_t。如圖:
核心模塊/配置模塊核心流程會只會調用核心模塊和配置模塊。
核心模塊調用各個其他模塊的core_module完成各自模塊的加載工作。配置模塊為其他模塊的基礎,負責解析配置文件。
負責請求連接的建立,分發等網絡事件及定時器事件,其中所有模塊封裝到ngx_events_module_t接口中供其他模塊直接調用。
http/stream/mail模塊對應nginx用戶功能的三個主體。以Http模塊為例,初始化,退出,對配置項的處理等工作也統一封裝在ngx_http_module_t中。http請求的處理過程各模塊可插拔,為固定的11個階段,模塊想介入,只需在ngx_http_module_t中定義回調函數,http協議內容多,對結果的處理也高度模塊化,根據配置項將模塊選擇性插入到輸出過濾鏈中。
系統調用nginx對各種操作系統的調用做了一層封裝,使模塊代碼無需區分。
依賴關系http,stream,mail依賴事件模塊,事件模塊依賴核心模塊和配置模塊,上層模塊依賴底層的系統調用。
3. 開發架構開發架構主要關注現有的代碼結構和開發代碼時如何擴展。先介紹代碼結構然后列舉一下如何新增模塊
代碼結構包含core,event,http,mail,misc,os,stream這幾個文件夾
core
為nginx的核心源代碼,包括main函數/整體控制,基本數據結構封裝,配置管理,內存管理,日志管理,文件讀寫網絡套接字功能,系統參數資源通用管理等
event
module子目錄實現了Nginx支持的事件驅動模型:AIOepollkqueueselectpoll等,其他提供了事件驅動模型相關數據結構的定義、初始化、事件接收傳遞管理功能以及時間驅動模型調用功能
http
module文件實現了http模塊的功能,perl為對perl的支持,v2為對http2.0的支持,其他提供結構定義、初始化、網絡連接建立、管理、關閉、數據報解析、upstream等通用功能
mail 郵件功能的實現
misc
os 根據操作系統對系統調用的封裝
stream 為對TCP/UDP反向代理的支持
開發擴展模塊編輯新的模塊步驟:
在自定義文件夾下,創建conf文件,說明名字和目錄。
編寫代碼。
編譯入nginx:
在configure腳本執行時加入參數--add-module=PATH,執行后會生成objs/Makefile,objs/ngx_modules.c(也可以直接修改)。會執行conf文件,生成的ngx_modules.c包含nginx啟動時加載的所有模塊,nginx核心代碼中全局modules從這里獲取。這個模塊就被編譯到nginx程序中了。
首先給出nginx的整體架構圖,然后介紹運行架構中關注的運行模式,通信方式,IO處理選型。再總結下nginx運行架構的特性:事件驅動+碎片化+異步處理
架構圖
說明:因為精力有限,只看了epoll的運行時架構,以下所有分析均只考慮linux并使用epoll的情況。
啟動后,有一個主進程,多個worker進行,兩個cache相關進程。多個worker共同監聽事件并處理,反向代理會把請求轉發給后端服務。
master: 管理worker等子進程實現重啟服務,平滑升級,更換日志文件,配置文件實時生效等
worker: 簡單的負載均衡(高負載等待),搶鎖,監聽處理事件,接收master命令
cache: nginx開啟緩存功能,會創建cache的兩個進程,cache loader在nginx啟動后將磁盤上次緩存的對象加載到內存后自動退出,cache管理進程清理超時緩存文件,限制緩存文件總大小,這個過程反反復復,直到Nginx整個進程退出。
進程間通信nginx的進程間通信,在不同應用場景下采取不同的形式:
linux與master/worker/cache進程通信:信號
master啟動時先把感興趣的信號注冊;
在主進程fork子進程之前要把所有信號調用sigprocmask阻塞住,等待fork成功后再將阻塞信號清除;
主進程之后就掛起在sigsuspend中,等待信號;
主進程與子進程通信:socketpair
這里每個子進程和父進程之間使用的是socketpair系統調用建立起來的全雙工的socket channel[]在父子進程中各有一套,channel[0]為寫端,channel[1]為讀端
父進程關閉socket[0], 子進程關閉socket[1],父進程從sockets[1]中讀寫,子進程從sockets[0]中讀寫,還是全雙工形態
子進程也會監督部分信號,是master通過socketpair發送過去的。linux關閉worker后,worker也會通過socketpair把信號發送給主進程
其他進程間共享數據:共享內存
nginx中所有共享內存都是以list鏈表的形式組織在全局變量cf->cycle->shared_memory下,在創建新的共享內存之前會先對該鏈表進行遍歷查找以及沖突檢測,對于已經存在且不存在沖突的共享內存可直接返回引用。
函數ngx_shm_alloc()時共享內存的實際分配,針對當前系統可提供的接口,可以是mmap,shmget等
應用于進程(如子進程之間,在進程重啟時新舊主進程需要搶鎖等)間需要共享的數據,比如連接數/互斥鎖等,另外提下鎖有多重互斥方式,在操作系統支持的情況下用優先用原子操作。
這部分為了并發需要考慮多進程,多線程,IO阻塞,IO非阻塞,每個進程處理一個還是多個事件 等典型的IO網絡選型中的這幾個問題。
nginx在操作系統支持的情況下(不支持根據不同操作系統和配置,事件模型中選擇不同IO處理方式)采取多進程,每個進程可以同時接收多個請求,IO多路復用非阻塞的方式。詳細的運行架構如圖。簡單抽象過程:主進程創建監聽socket后所有worker子進程繼承共同監聽,通過搶鎖的方式決定同一時刻哪個worker是請求的acceptor方,accept請求后在本子進程中處理。
多進程
為了并發和利用多核處理,首先啟用多進程的模式,在主進程創建所有監聽的socket,為了所有worker都可以繼承并監聽該socket的fd。
多acceptor,多handler 采取多acceptor,多handler的模式,每個進程在自己內部acceptor后分配給自己內部的handler處理。為了防止多acceptor同時accept的驚群現象,只有搶到鎖的才把事件加入到監聽,喚醒只會喚醒當前進程accept事件(新版的nginx采取reuseport可以一個端口被多個進程監聽,支持的4.3的accept相關特征也不需要搶鎖)
進程accept多個 每個進程可以accept多個連接的模式,每次只處理accept,為三次握手完成的請求建立連接后就將其他事件放入延遲隊列,釋放鎖后才處理這部分隊列,以便其他進程可以繼續搶鎖
worker監聽處理的過程如圖所示,在master啟動的時候,為每個端口創建監聽套接字listen socked(以下簡稱lss),然后fork出worker進程,所有worker進程繼承同一份lss,為每個ls創建連接和事件結構,每個空閑worker搶鎖獲取這些lss的處理權。持有鎖就將ls的讀事件加入到epoll中等待,把接收事件分為兩類:優先處理accept,延時處理非建立連接事件。accept后就釋放鎖。
worker會有三種情況,一種是空閑但沒有搶到鎖,就等待事件后繼續搶鎖。另一種是在處理隊列中請求,空閑后去搶鎖。第三種是空閑并且搶到鎖,則持有鎖并監聽和分配給hander后釋放鎖,處理隊列請求
事件驅動+碎片化+異步處理
nginx所有需要等待的全都盡可能的碎片化,并加入到事件中,當事件ready后根據回調調用消費者處理,在Nginx里,Listen后是不需要循環等待accept,把他加入到epoll中,統一在epoll_wait中處理,當有返回直接調用accept。包括后續與客戶端建立的主動連接(非Listen的)的所有事件也都統一在epoll_wait中等待,有事件直接調用事件的消費回調函數。在調用epoll_wait時也是一直循環等待事件沒有退出,所以就要把事件拆分成特別細小的單元,這些單元都是可以異步執行的,有了epoll這個模型可以把任何涉及到磁盤讀寫的小粒度事件加入到監控中,比如讀過了頭第一行就去處理headline把其他的再加入到epoll中。
考慮如果沒有nginx,自己實現,是如何實現。
先聊運行架構
要實現的簡化功能概述:服務器要持續啟動,監聽8000端口,收到請求后解析http協議,若是靜態請求,獲取文件內容,封裝為Http的響應協議格式,發送;若為動態請求,轉化為fastcgi協議,轉發給fastcgi程序,發送到響應的端口,獲取數據后再轉化為Http響應協議格式發送。
為了實現高并發,當然要開啟多個處理進程,因為要監聽同一個端口,需要一個監聽進程負責監聽端口(在不考慮新的技術支持多個進程同時監聽一個端口的情況),accept后分配給處理進程。對于單個監聽端口最好設計是單acceptor多進程的形式,然而,這基本上是不可能實現的,因為多個進程處理完的數據如何返回給監聽進程,大量的數據再進程間通信是不現實的。因此對于單個監聽端口只能是單進程。或者改用線程,然而多線程不穩定。
我可以對多個監聽端口開啟多個進程,每個監聽不同的端口,但端口間流量分配不均勻時,進程負載不均衡。
=》從監聽處理進程個數上,nginx比我自己設計聰明的地方體現出來了,特殊的多reactor多進程結構。
再說說每個進程里的高并發,網絡連接肯定會有IO等待,此時若可以繼續做其他的,會更快。每次接收一個請求的情況下,可以讀完請求后,一邊請求異步磁盤一邊寫返回頭等;在有子請求和接受多個請求的情況下,可以一邊為其他請求建立連接,一邊處理本請求的事件,多個請求同時處理。因此設計為單進程可以同時accept多個,每個可以并行的操作都拆為多帶帶事件異步處理。思想和nginx一樣。
再聊聊開發架構,代碼架構可以依照開發架構
nginx因為支持反向代理,支持多平臺,支持自定義配置,所以有配置模塊,統一的事件模塊,有抽象的配置結構。自己開發web服務器,可能不會考慮這么多,主要考慮http的處理過程,http固定的讀取頭,解析頭,真實ip,權限,處理,輸出協議的轉換,寫日志做成固定的順序,直接調用固定函數。nginx再此之上,每個過程有自己的回調,整體階段清晰,每個模塊可以在把回調加入到各個子階段,更靈活。協議按順序也先固定輸出鏈,若沒有該協議直接跳過,對于這種運行前不知道會有哪些輸出過濾的情況,自己寫可能就在運行中判斷有就調用了,nginx是固定走,沒有直接跳過,這兩種根據不同應用各有應用吧。
高可擴展性模塊化
無論配置,初始化,代碼結構都是模塊化的,各個模塊要介入到主流程,根本不需要修改主流程代碼,通過在hook位置增加回調。
高度抽象
正常很難想到所有的模塊和配置全部都一個抽象結構,各個子模塊也都統一抽象結構,新增加功能簡單,可讀性高
輸出統一過濾鏈,功能可插拔
擴展容易,代碼簡潔,可讀性高
高性能多reactor多進程結構
經過上述分析,Nginx在并發選型上要么是單reactor單進程結構,要么是單reactor多線程結構,但多線程只要一個操作共享區域,會影響其他線程,所以在不需要共享數據的情況下,最好用多進程。nginx巧妙的雖然同一時刻只能單reactor,但在accept后立刻釋放鎖,也達到多reactor的性能,此架構不常見,可以參考。memcache,mysql等因為要共享數據都是多reactor多線程;apache舊版是一個進程處理一個請求,類似phpfpm,本質上是單reactor單進程,后來一個進程中有多個線程,單reactor多線程,但每個線程處理一個請求;后面也加入了IO多路復用,每個線程中處理多個請求。
后續preactor+線程
本質上epoll還是等待的,還是需要進程去詢問,利用內核異步IO,可以做到事件自動處理,處理后通知,不需要詢問,其架構如下:
單linux的AIO還不完善,到目前為止,nginx實現了AIO+線程的模型,但還未應用。
內存池,連接池
為了省去每次申請,減少內存碎片,統一釋放等,提前準備好內存池和連接池 。
四.總結nginx作為一個高性能高可用高可擴展的 http服務器和多協議反向代理服務器,其運行架構采用特殊的監聽同一端口卻多reactor多進程的模型,值得借鑒;高度抽象和模塊化的邏輯架構使得功能龐大代碼卻清晰易懂,開發和擴展代價低。
參考
nginx功能 http://nginx.org/en/
nginx代碼結構 https://www.kancloud.cn/diges...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/40247.html
摘要:而且在負載上面遠超,為什么目前大多數的互聯網公司都是使用這種架構模式,而不是直接,這樣不是架構更加方便,而且性能更佳優異嘛。其特點是占有內存少,并發能力強。 最近有人問我,Nginx有動態分離機制,靜態請求直接就可以通過Nginx處理,動態請求才轉發請求到后臺交由Tomcat進行處理。而且Nginx在負載上面遠超Apache,為什么目前大多數的互聯網公司都是使用Nginx+Apache...
摘要:而且在負載上面遠超,為什么目前大多數的互聯網公司都是使用這種架構模式,而不是直接,這樣不是架構更加方便,而且性能更佳優異嘛。其特點是占有內存少,并發能力強。 最近有人問我,Nginx有動態分離機制,靜態請求直接就可以通過Nginx處理,動態請求才轉發請求到后臺交由Tomcat進行處理。而且Nginx在負載上面遠超Apache,為什么目前大多數的互聯網公司都是使用Nginx+Apache...
摘要:請求的多階段異步處理多階段異步處理請求與事件驅動架構是密切相關的,也就是說,請求的多階段異步處理只能基于事件驅動架構實現。 前言 最近在讀 Nginx 相關的書籍,做一下讀書筆記。 Nginx 作為業界知名的高性能服務器,被廣泛的應用。它的高性能正是由于其優秀的架構設計,其架構主要包括這幾點:模塊化設計、事件驅動架構、請求的多階段異步處理、管理進程與多工作進程設計、內存池的設計,以下內...
閱讀 2412·2021-11-11 16:54
閱讀 1213·2021-09-22 15:23
閱讀 3656·2021-09-07 09:59
閱讀 2007·2021-09-02 15:41
閱讀 3292·2021-08-17 10:13
閱讀 3054·2019-08-30 15:53
閱讀 1242·2019-08-30 13:57
閱讀 1216·2019-08-29 15:16