【3.工程開發】-nginx架構

一.概述

本文將深刻剖析nginx的架構。php

第一部分介紹nginx現有框架,用典型的4+1視圖闡述,包括邏輯架構,開發架構,運行架構,物理架構,功能用例,nginx爲單機服務,不考慮物理架構。其中功能用例概述nginx功能;邏輯架構主要介紹nginx高度模塊化中各個模塊的分層和依賴關係;開發架構主要描述nginx的代碼結構和代碼內容簡介;重點是運行架構,nginx一主多從的進程模型架構和通訊,高併發進程和IO併發的選型等。html

第二部分對比nginx運行架構和其餘開源運行架構,總結nginx爲什麼要這樣選型;介紹nginx邏輯架構中的優勢。mysql

本文適合閱讀對象:1)已經看過nginx代碼,本文幫你高度抽象總結了nginx的架構和與我本身設計相比較,nginx哪裏設計的優勢,試着從架構層來從新看下代碼;2)研究各類系統架構的人,本文從統一的架構視圖介紹,無需知道nginx的代碼細節,列出了與其餘架構比,nginx架構的亮點。3)還未看過nginx的代碼,關注第二章,能夠看四個視圖忽略對特性的分析和架構的思考,幫助瞭解nginx有什麼功能、如何組織代碼、如何運行。react

關鍵詞:Nginx架構,nginx功能,nginx邏輯架構,nginx代碼結構,nginx運行架構,nginx高性能實現

二.nginx現有架構實現

功能介紹

nginx最核心的功能是web服務器和反向代理服務器,web服務器完成對 http請求協議的解析 和 以http協議格式響應請求、緩存、日誌處理這些 基本web服務器 功能,反向代理服務器完成對請求的轉發、負載均衡、鑑權、限流、緩存、日誌處理等代理經常使用功能。nginx在這方面提供了豐富的功能,包括對http2,ssl等等的支持。除了http外,nginx還支持mail服務和普通的tcp,udp協議的反向代理功能。一下列出了經常使用功能,詳細全部功能見參考1
### http服務器/反向代理服務器linux

  • 靜態文件,fastcgi,uwsgi,scgi,memcached 服務
  • 緩存
  • 負載均衡
  • SSL/TLS
  • HTTP2
  • 鑑權/限流
  • 虛擬servers

功能用例舉例:web服務器和反向代理服務器的功能(第一個locatiion爲服務器,後面是反向代理)。在nginx中配置如圖配置,啓動nginx加載配置後,發起http請求,獲取服務器響應。nginx

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
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</html>
* Connection #0 to host localhost left intact
* Closing connection #0

mail反向代理

  • mail反向代理
  • SSL; STARTTLS/STLS

TCP/UDP反向代理 socket,websocket

  • SSL/TLS, 負載均衡, 鑑權/限流等

2.邏輯架構

nginx在邏輯上分爲入口層,模塊化的功能處理層,系統調用層。入口調用配置模塊和核心模塊,各核心模塊分別調用各自功能模塊,系統調用層封裝了各個操做系統的功能被功能處理層使用。邏輯架構最明顯主要的特徵就是高度模塊化,全部功能都是模塊,每一個模塊都統一結構,下面先看下這個統一結構,而後分別介紹各個模塊。
邏輯架構web

特徵——高度模塊化

nginx除了main等少許代碼,其餘全都是模塊,全部模塊都是Ngx_module_t的抽象,只有初始化,退出,對配置項的處理;每一個模塊內部也都有本身模塊ngx_xx_module_t的抽象;配置也高度抽象統一的結構ngx_command_t。如圖:sql

特徵

核心模塊/配置模塊

核心流程會只會調用核心模塊和配置模塊。
核心模塊調用各個其餘模塊的core_module完成各自模塊的加載工做。配置模塊爲其餘模塊的基礎,負責解析配置文件。apache

事件模塊

負責請求鏈接的創建,分發等網絡事件及定時器事件,其中全部模塊封裝到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反向代理的支持

開發擴展模塊

編輯新的模塊步驟:

  1. 在自定義文件夾下,建立conf文件,說明名字和目錄。
  2. 編寫代碼。
  3. 編譯入nginx:
    在configure腳本執行時加入參數--add-module=PATH,執行後會生成objs/Makefile,objs/ngx_modules.c(也能夠直接修改)。會執行conf文件,生成的ngx_modules.c包含nginx啓動時加載的全部模塊,nginx核心代碼中全局modules從這裏獲取。這個模塊就被編譯到nginx程序中了。

4.運行架構

首先給出nginx的總體架構圖,而後介紹運行架構中關注的運行模式,通訊方式,IO處理選型。再總結下nginx運行架構的特性:事件驅動+碎片化+異步處理

架構圖

運行架構
說明:由於精力有限,只看了epoll的運行時架構,如下全部分析均只考慮linux並使用epoll的狀況。
啓動後,有一個主進程,多個worker進行,兩個cache相關進程。多個worker共同監聽事件並處理,反向代理會把請求轉發給後端服務。

一主+多worker+cache manager+cache loader進程

  • 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非阻塞,每一個進程處理一個仍是多個事件 等典型的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架構的思考分析

考慮若是沒有nginx,本身實現,是如何實現。

  1. 先聊運行架構

    要實現的簡化功能概述:服務器要持續啓動,監聽8000端口,收到請求後解析http協議,如果靜態請求,獲取文件內容,封裝爲Http的響應協議格式,發送;若爲動態請求,轉化爲fastcgi協議,轉發給fastcgi程序,發送到響應的端口,獲取數據後再轉化爲Http響應協議格式發送。
    爲了實現高併發,固然要開啓多個處理進程,由於要監聽同一個端口,須要一個監聽進程負責監聽端口(在不考慮新的技術支持多個進程同時監聽一個端口的狀況),accept後分配給處理進程。對於單個監聽端口最好設計是單acceptor多進程的形式,然而,這基本上是不可能實現的,由於多個進程處理完的數據如何返回給監聽進程,大量的數據再進程間通訊是不現實的。所以對於單個監聽端口只能是單進程。或者改用線程,然而多線程不穩定。

    我能夠對多個監聽端口開啓多個進程,每一個監聽不一樣的端口,但端口間流量分配不均勻時,進程負載不均衡。

    =》從監聽處理進程個數上,nginx比我本身設計聰明的地方體現出來了,特殊的多reactor多進程結構。

    再說說每一個進程裏的高併發,網絡鏈接確定會有IO等待,此時若能夠繼續作其餘的,會更快。每次接收一個請求的狀況下,能夠讀完請求後,一邊請求異步磁盤一邊寫返回頭等;在有子請求和接受多個請求的狀況下,能夠一邊爲其餘請求創建鏈接,一邊處理本請求的事件,多個請求同時處理。所以設計爲單進程能夠同時accept多個,每一個能夠並行的操做都拆爲單獨事件異步處理。思想和nginx同樣。

  2. 再聊聊開發架構,代碼架構能夠依照開發架構

    nginx由於支持反向代理,支持多平臺,支持自定義配置,因此有配置模塊,統一的事件模塊,有抽象的配置結構。本身開發web服務器,可能不會考慮這麼多,主要考慮http的處理過程,http固定的讀取頭,解析頭,真實ip,權限,處理,輸出協議的轉換,寫日誌作成固定的順序,直接調用固定函數。nginx再此之上,每一個過程有本身的回調,總體階段清晰,每一個模塊能夠在把回調加入到各個子階段,更靈活。協議按順序也先固定輸出鏈,若沒有該協議直接跳過,對於這種運行前不知道會有哪些輸出過濾的狀況,本身寫可能就在運行中判斷有就調用了,nginx是固定走,沒有直接跳過,這兩種根據不一樣應用各有應用吧。

高可擴展性

  1. 模塊化

    不管配置,初始化,代碼結構都是模塊化的,各個模塊要介入到主流程,根本不須要修改主流程代碼,經過在hook位置增長回調。

  2. 高度抽象

    正常很難想到全部的模塊和配置所有都一個抽象結構,各個子模塊也都統一抽象結構,新增長功能簡單,可讀性高

  3. 輸出統一過濾鏈,功能可插拔

    擴展容易,代碼簡潔,可讀性高

高性能

  1. 多reactor多進程結構

    通過上述分析,Nginx在併發選型上要麼是單reactor單進程結構,要麼是單reactor多線程結構,但多線程只要一個操做共享區域,會影響其餘線程,因此在不須要共享數據的狀況下,最好用多進程。nginx巧妙的雖然同一時刻只能單reactor,但在accept後馬上釋放鎖,也達到多reactor的性能,此架構不常見,能夠參考。memcache,mysql等由於要共享數據都是多reactor多線程;apache舊版是一個進程處理一個請求,相似phpfpm,本質上是單reactor單進程,後來一個進程中有多個線程,單reactor多線程,但每一個線程處理一個請求;後面也加入了IO多路複用,每一個線程中處理多個請求。

  2. 後續preactor+線程

preactor

本質上epoll仍是等待的,仍是須要進程去詢問,利用內核異步IO,能夠作到事件自動處理,處理後通知,不須要詢問,其架構以下:

單linux的AIO還不完善,到目前爲止,nginx實現了AIO+線程的模型,但還未應用。

  1. 內存池,鏈接池

    爲了省去每次申請,減小內存碎片,統一釋放等,提早準備好內存池和鏈接池 。

四.總結

nginx做爲一個高性能高可用高可擴展的 http服務器和多協議反向代理服務器,其運行架構採用特殊的監聽同一端口卻多reactor多進程的模型,值得借鑑;高度抽象和模塊化的邏輯架構使得功能龐大代碼卻清晰易懂,開發和擴展代價低。

  • 參考

nginx功能 http://nginx.org/en/
nginx代碼結構 https://www.kancloud.cn/diges...

相關文章
相關標籤/搜索