1. 程式人生 > >nginx 工作原理,程序模型,事件處理,配置系統和模組化體系

nginx 工作原理,程序模型,事件處理,配置系統和模組化體系

nginx在啟動後,在unix系統中會以daemon的方式在後臺執行,後臺程序包含一個master程序和多個worker程序。

當然nginx也是支援多執行緒的方式的,只是我們主流的方式還是多程序的方式,也是nginx的預設方式。nginx採用多程序的方式有諸多好處,所以我就主要講解nginx的多程序模式吧。

nginx在啟動後,會有一個master程序和多個worker程序。master程序主要用來管理worker程序,包含:接收來自外界的訊號,向各worker程序傳送訊號,監控worker程序的執行狀態,當worker程序退出後(異常情況下),會自動重新啟動新的worker程序。而基本的網路事件,則是放在worker程序中來處理了。多個worker程序之間是對等的,他們同等競爭來自客戶端的請求,各程序互相之間是獨立的。一個請求,只可能在一個worker程序中處理,一個worker程序,不可能處理其它程序的請求。worker程序的個數是可以設定的,一般我們會設定與機器cpu核數一致,這裡面的原因與nginx的程序模型以及事件處理模型是分不開的。nginx的程序模型,可以由下圖來表示:

nginx程序模型的好處:

  1. 對於每個worker程序來說,獨立的程序,不需要加鎖,所以省掉了鎖帶來的開銷,同時在程式設計以及問題查詢時,也會方便很多。
  2. 採用獨立的程序,可以讓互相之間不會影響,一個程序退出後,其它程序還在工作,服務不會中斷,master程序則很快啟動新的worker程序。
  3. 如果worker程序的異常退出,肯定是程式有bug了,異常退出,會導致當前worker上的所有請求失敗,不過不會影響到所有請求,所以降低了風險。

Nginx的事件處理過程

有人可能要問了,nginx採用多worker的方式來處理請求,每個worker裡面只有一個主執行緒,那能夠處理的併發數很有限啊,多少個worker就能處理多少個併發,何來高併發呢?

首先,請求過來,要建立連線,然後再接收資料,接收資料後,再發送資料。具體到系統底層,就是讀寫事件,而當讀寫事件沒有準備好時,必然不可操作,如果不用非阻塞的方式來呼叫,那就得阻塞呼叫了,事件沒有準備好,那就只能等了,等事件準備好了,你再繼續吧。阻塞呼叫會進入核心等待,cpu就會讓出去給別人用了,對單執行緒的worker來說,顯然不合適,當網路事件越多時,大家都在等待呢,cpu空閒下來沒人用,cpu利用率自然上不去了,更別談高併發了。好吧,你說加程序數,這跟apache的執行緒模型有什麼區別,注意,別增加無謂的上下文切換。所以,在nginx裡面,最忌諱阻塞的系統呼叫了。不要阻塞,那就非阻塞嘍。非阻塞就是,事件沒有準備好,馬上返回EAGAIN,告訴你,事件還沒準備好呢,你慌什麼,過會再來吧。好吧,你過一會,再來檢查一下事件,直到事件準備好了為止,在這期間,你就可以先去做其它事情,然後再來看看事件好了沒。雖然不阻塞了,但你得不時地過來檢查一下事件的狀態,你可以做更多的事情了,但帶來的開銷也是不小的。所以,才會有了非同步非阻塞的事件處理機制,具體到系統呼叫就是像select/poll/epoll/kqueue這樣的系統呼叫。它們提供了一種機制,讓你可以同時監控多個事件,呼叫他們是阻塞的,但可以設定超時時間,在超時時間之內,如果有事件準備好了,就返回。這種機制正好解決了我們上面的兩個問題,拿epoll為例(在後面的例子中,我們多以epoll為例子,以代表這一類函式),當事件沒準備好時,放到epoll裡面,事件準備好了,我們就去讀寫,當讀寫返回EAGAIN時,我們將它再次加入到epoll裡面。這樣,只要有事件準備好了,我們就去處理它,只有當所有事件都沒準備好時,才在epoll裡面等著。這樣,我們就可以併發處理大量的併發了,當然,這裡的併發請求,是指未處理完的請求,執行緒只有一個,所以同時能處理的請求當然只有一個了,只是在請求間進行不斷地切換而已,切換也是因為非同步事件未準備好,而主動讓出的。這裡的切換是沒有任何代價,你可以理解為迴圈處理多個準備好的事件,事實上就是這樣的。與多執行緒相比,這種事件處理方式是有很大的優勢的,不需要建立執行緒,每個請求佔用的記憶體也很少,沒有上下文切換,事件處理非常的輕量級。併發數再多也不會導致無謂的資源浪費(上下文切換)。更多的併發數,只是會佔用更多的記憶體而已。 我之前有對連線數進行過測試,在24G記憶體的機器上,處理的併發請求數達到過200萬。現在的網路伺服器基本都採用這種方式,這也是nginx效能高效的主要原因。

我們之前說過,推薦設定worker的個數為cpu的核數,在這裡就很容易理解了,更多的worker數,只會導致程序來競爭cpu資源了,從而帶來不必要的上下文切換。而且,nginx為了更好的利用多核特性,提供了cpu親緣性的繫結選項,我們可以將某一個程序繫結在某一個核上,這樣就不會因為程序的切換帶來cache的失效。像這種小的優化在nginx中非常常見,同時也說明了nginx作者的苦心孤詣。比如,nginx在做4個位元組的字串比較時,會將4個字元轉換成一個int型,再作比較,以減少cpu的指令數等等。

現在,知道了nginx為什麼會選擇這樣的程序模型與事件模型了。對於一個基本的web伺服器來說,事件通常有三種類型,網路事件、訊號、定時器。從上面的講解中知道,網路事件通過非同步非阻塞可以很好的解決掉。如何處理訊號與定時器?

首先,訊號的處理。對nginx來說,有一些特定的訊號,代表著特定的意義。訊號會中斷掉程式當前的執行,在改變狀態後,繼續執行。如果是系統呼叫,則可能會導致系統呼叫的失敗,需要重入。關於訊號的處理,大家可以學習一些專業書籍,這裡不多說。對於nginx來說,如果nginx正在等待事件(epoll_wait時),如果程式收到訊號,在訊號處理函式處理完後,epoll_wait會返回錯誤,然後程式可再次進入epoll_wait呼叫。

另外,再來看看定時器。由於epoll_wait等函式在呼叫的時候是可以設定一個超時時間的,所以nginx藉助這個超時時間來實現定時器。nginx裡面的定時器事件是放在一顆維護定時器的紅黑樹裡面,每次在進入epoll_wait前,先從該紅黑樹裡面拿到所有定時器事件的最小時間,在計算出epoll_wait的超時時間後進入epoll_wait。所以,當沒有事件產生,也沒有中斷訊號時,epoll_wait會超時,也就是說,定時器事件到了。這時,nginx會檢查所有的超時事件,將他們的狀態設定為超時,然後再去處理網路事件。由此可以看出,當我們寫我們可以用一段虛擬碼來總結一下nginx的事件處理模型:nginx程式碼時,在處理網路事件的回撥函式時,通常做的第一個事情就是判斷超時,然後再去處理網路事件。

我們可以用一段虛擬碼來總結一下nginx的事件處理模型:

while (true) {
    for t in run_tasks:
        t.handler();
    update_time(&now);
    timeout = ETERNITY;
    for t in wait_tasks: /* sorted already */
        if (t.time <= now) {
            t.timeout_handler();
        } else {
            timeout = t.time - now;
            break;
        }
    nevents = poll_function(events, timeout);
    for i in nevents:
        task t;
        if (events[i].type == READ) {
            t.handler = read_handler;
        } else { /* events[i].type == WRITE */
            t.handler = write_handler;
        }
        run_tasks_add(t);
}

Nginx配置系統

Nginx的配置系統由一個主配置檔案和一些輔助配置檔案構成,這些配置檔案預設在/usr/local/nginx/目錄下。

nginx的配置系統由一個主配置檔案和其他一些輔助的配置檔案構成。這些配置檔案均是純文字檔案,全部位於nginx安裝目錄下的conf目錄下。

配置檔案中以#開始的行,或者是前面有若干空格或者TAB,然後再跟#的行,都被認為是註釋,也就是隻對編輯檢視檔案的使用者有意義,程式在讀取這些註釋行的時候,其實際的內容是被忽略的。

由於除主配置檔案nginx.conf以外的檔案都是在某些情況下才使用的,而只有主配置檔案是在任何情況下都被使用的。所以在這裡我們就以主配置檔案為例,來解釋nginx的配置系統。

在nginx.conf中,包含若干配置項。每個配置項由配置指令和指令引數2個部分構成。指令引數也就是配置指令對應的配置值。

指令概述

配置指令是一個字串,可以用單引號或者雙引號括起來,也可以不括。但是如果配置指令包含空格,一定要引起來。

指令引數

指令的引數使用一個或者多個空格或者TAB字元與指令分開。指令的引數有一個或者多個TOKEN串組成。TOKEN串之間由空格或者TAB鍵分隔。

TOKEN串分為簡單字串或者是複合配置塊。複合配置塊即是由大括號括起來的一堆內容。一個複合配置塊中可能包含若干其他的配置指令。

如果一個配置指令的引數全部由簡單字串構成,也就是不包含複合配置塊,那麼我們就說這個配置指令是一個簡單配置項,否則稱之為複雜配置項。例如下面這個是一個簡單配置項:

error_page   500 502 503 504  /50x.html;

對於簡單配置,配置項的結尾使用分號結束。對於複雜配置項,包含多個TOKEN串的,一般都是簡單TOKEN串放在前面,複合配置塊一般位於最後,而且其結尾,並不需要再新增分號。例如下面這個複雜配置項

location / {
    root   /home/jizhao/nginx-book/build/html;
    index  index.html index.htm;
}

指令上下文¶

nginx.conf中的配置資訊,根據其邏輯上的意義對其進行分類,可以分成多個作用域或指令上下文,指令上下文層次關係如下:

  • main:Nginx在執行時與具體業務功能無關的引數,比如工作程序數、執行身份等。

  • http:與提供http服務相關的引數,比如keepalive、gzip等。

  • server:http服務上支援若干虛擬機器,每個虛擬機器一個對應的server配置項,配置項裡包含該虛擬機器相關的配置。

  • location:http服務中,某些特定的URL對應的一系列配置項。

  • mail:實現email相關的SMTP/IMAP/POP3代理時,共享的一些配置項。

更多配置資訊可以參看Nginx安裝後的預設配置檔案/usr/local/nginx/nginx.conf.default。

指令上下文,可能有包含的情況出現。例如:通常http上下文和mail上下文一定是出現在main上下文裡的。在一個上下文裡,可能包含另外一種型別的上下文多次。例如:如果http服務,支援了多個虛擬主機,那麼在http上下文裡,就會出現多個server上下文。

user  nobody;
worker_processes  1;
error_log  logs/error.log  info;

events {
    worker_connections  1024;
}

http {
    server {
        listen          80;
        server_name     www.linuxidc.com;
        access_log      logs/linuxidc.access.log main;
        location / {
            index index.html;
            root  /var/www/linuxidc.com/htdocs;
        }
    }

    server {
        listen          80;
        server_name     www.Androidj.com;
        access_log      logs/androidj.access.log main;
        location / {
            index index.html;
            root  /var/www/androidj.com/htdocs;
        }
    }
}

mail {
    auth_http  127.0.0.1:80/auth.php;
    pop3_capabilities  "TOP"  "USER";
    imap_capabilities  "IMAP4rev1"  "UIDPLUS";

    server {
        listen     110;
        protocol   pop3;
        proxy      on;
    }
    server {
        listen      25;
        protocol    smtp;
        proxy       on;
        smtp_auth   login plain;
        xclient     off;
    }
}

在這個配置中,上面提到個五種配置指令上下文都存在.

存在於main上下文中的配置指令如下:

  • user
  • worker_processes
  • error_log
  • events
  • http
  • mail

存在於http上下文中的指令如下:

  • server

存在於mail上下文中的指令如下

  • server
  • auth_http
  • imap_capabilities

存在於server上下文中的配置指令如下:

  • listen
  • server_name
  • access_log
  • location
  • protocol
  • proxy
  • smtp_auth
  • xclient

存在於location上下文中的指令如下

  • index
  • root
當然,這裡只是一些示例。具體有哪些配置指令,以及這些配置指令可以出現在什麼樣的上下文中,需要參考nginx的使用文件.

Nginx的模組化體系

一、模組體系

Nginx的內部結構是由核心部分和一系列功能模組組成的,這樣可以使得每個模組的功能相對簡單,便於對系統進行功能擴充套件,各模組之間的關係如下圖:

nginx core實現了底層的通訊協議,為其它模組和Nginx程序構建了基本的執行時環境,並且構建了其它各模組的協作基礎。

http模組和mail模組位於nginx core和各功能模組的中間層,這2個模組在nginx core之上實現了另外一層抽象,分別處理與http協議和email相關協議(SMTP/IMAP/POP3)有關的事件,並且確保這些事件能被以正確的順序呼叫其它的一些功能模組。

nginx功能模組基本上分為如下幾種型別:

(1)event module:搭建了獨立於作業系統的事件處理機制的框架,以及提供了各具體事件的處理,包括ngx_event_module、ngx_event_core_module和ngx_epoll_module等,Nginx具體使用何種事件處理模組,這依賴於具體的作業系統和編譯選項。

(2)phase handler:此型別的模組也被直接稱為handler模組,主要負責處理客戶端請求併產生待響應內容,比如ngx_http_module模組,負責客戶端的靜態頁面請求處理並將對應的磁碟檔案準備為響應內容輸出。

(3)output filter:也稱為filter模組,主要是負責對輸出的內容進行處理,可以對輸出進行修改,比如可以實現對輸出的所有html頁面增加預定義的footbar一類的工作,或者對輸出的圖片的URL進行替換之類的工作。

(4)upstream:實現反向代理功能,將真正的請求轉發到後端伺服器上,並從後端伺服器上讀取響應,發回客戶端,upstream模組是一種特殊的handler,只不過響應內容不是真正由自己產生的,而是從後端伺服器上讀取的。

(5)load-balancer:負載均衡模組,實現特定的演算法,在眾多的後端伺服器中,選擇一個伺服器出來作為某個請求的轉發伺服器。

(6)extend module:根據特定業務需要編寫的第三方模組。

二、請求處理

下面將會以http請求處理為例來說明請求、配置和模組是如何串起來的。

當Nginx讀取到一個HTTP Request的header時,首先查詢與這個請求關聯的虛擬主機的配置,如果找到了則這個請求將會經歷以下幾個階段的處理(phase handlers):

NGX_HTTP_POST_READ_PHASE:讀取請求內容階段

NGX_HTTP_SERVER_REWRITE_PHASE:Server請求地址重寫階段

NGX_HTTP_FIND_CONFIG_PHASE:配置查詢階段

NGX_HTTP_REWRITE_PHASE:Location請求地址重寫階段

NGX_HTTP_POST_REWRITE_PHASE:請求地址重寫提交階段

NGX_HTTP_PREACCESS_PHASE:訪問許可權檢查準備階段

NGX_HTTP_ACCESS_PHASE:訪問許可權檢查階段

NGX_HTTP_POST_ACCESS_PHASE:訪問許可權檢查提交階段

NGX_HTTP_TRY_FILES_PHASE:配置項try_files處理階段

NGX_HTTP_CONTENT_PHASE:內容產生階段

NGX_HTTP_LOG_PHASE:日誌模組處理階段

在內容產生階段,為了給一個request產生正確的response,Nginx必須把這個請求交給一個合適的content handler去處理。如果這個request對應的location在配置檔案中被明確指定了一個content handler,那麼Nginx就可以通過對location的匹配,直接找到這個對應的handler,並把這個request交給這個content handler去處理。這樣的配置指令包括perl、flv、proxy_pass、mp4等。

如果一個request對應的location並沒有直接配置的content handler,那麼Nginx依次作如下嘗試:

(1)如果一個location裡面有配置random_index on,那麼隨機選擇一個檔案傳送給客戶端。

(2)如果一個location裡面有配置index指令,那麼傳送index指令指定的檔案給客戶端。

(3)如果一個location裡面有配置autoindex on,那麼就傳送請求地址對應的服務端路徑下的檔案列表給客戶端。

(4)如果這個request對應的location上有設定gzip_static on,那麼就查詢是否有對應的.gz檔案存在,如果有的話,就傳送這個給客戶端(客戶端支援gzip的情況下)。

(5)請求的URI如果對應一個靜態檔案,static module就傳送靜態檔案的內容到客戶端。

內容產生階段完成以後,生成的輸出會被傳遞到filter模組去進行處理。filter模組也是與location相關的。所有的fiter模組都被組織成一條鏈。輸出會依次穿越所有的filter,直到有一個filter模組的返回值表明已經處理完成。接下來就可以傳送response給客戶端了。

相關推薦

nginx 工作原理程序模型事件處理配置系統模組體系

nginx在啟動後,在unix系統中會以daemon的方式在後臺執行,後臺程序包含一個master程序和多個worker程序。 當然nginx也是支援多執行緒的方式的,只是我們主流的方式還是多程序的

Nginx 工作原理

cti nec 需要 led 流程圖 技術 模型 方式 png Nginx 工作原理 Nginx由內核和模塊組成。   Nginx本身做的工作實際很少,當它接到一個HTTP請求時,它僅僅是通過查找配置文件將此次請求映射到一個location block,而此location

Nginx工作原理及相關介紹

handle 利用 ice fff 高並發 解析器 異常 creat 為什麽 Nginx工作原理及相關介紹一、Nginx工作原理與模塊介紹1、Nginx基本工作原理NGINX以高性能的負載均衡器,緩存,和web服務器聞名。Nginx由內核和模塊組成,其中,內核的設計非常微小

js基礎知識:閉包事件處理原型

bsp ret asc 原函數 ati tac ons 標識符 構造 閉包:其實就是js代碼在執行的時候會創建變量對象的一個作用域鏈,標識符解析的時候會沿著作用域鏈一級一級的網上搜索,最後到達全局變量停止。所以某個函數可以訪問外層的局部變量和全局變量,但是訪問不了裏層的變量

nginx 工作原理配置文件講解

打開 cli ssi http 狀態碼 stat pro clu libs red 1、nginx 介紹 Nginx (engine x) 是一個高性能的HTTP和反向代理服務,也是一個IMAP/POP3/SMTP服務。Nginx是由伊戈爾·賽索耶夫為俄羅斯訪問量第二的Ra

Nginx工作原理優化 漏洞

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

event.cancelBubble=true e.stopPropagation() 取消事件處理阻止事件

<tr><a href="xxx">連線</a></tr> 如上結構,單擊tr的時候跳轉至另一頁   <tr style="cursor:pointer" onmouseover="this.style

react事件處理為類方法繫結this

react事件繫結屬性的命名採用駝峰命名法, 如果採用JSX的語法,需要傳入一個函式作為事件處理函式,而不是一個字串(DOM元素的寫法)。 阻止事件的預設行為: 不能使用返回false的方式來阻止預設行為,必須明確的使用preventDefault 在類方法中繫

Fragment中RecyclerView的使用解析以及監聽事件處理

RecyclerView是可以代替listview使用的新元件,個人感覺其主要特色:其介面卡adapter中,重寫的東西少了,頁面展示的效果跟加多了,比如可以在RecyclerView設定listview的顯示效果,也可以設定gridview的顯示效果,也可以設定瀑布流的顯示效果!下面程式碼主要

【js事件詳解】js事件封裝函式js跨瀏覽器事件處理機制

一、事件流 事件流描述的是從頁面中接受事件的順序。 IE的事件流是事件冒泡流,而Netscape的事件流是事件捕獲流 1、事件冒泡 事件冒泡,即事件最開始由最具體的元素(文件中巢狀層次最深的那個節點)接收,然後逐級向上轉播至最不具體的節點(文件)。 2、事件捕獲 事件捕獲的

nginx工作原理實現高併發請求的原因

一、程序、執行緒?程序是具有一定獨立功能的,在計算機中已經執行的程式的實體。在早期系統中(如linux 2.4以前),程序是基本運作單位,在支援執行緒的系統中(如windows,linux2.6)中,執行緒才是基本的運作單位,而程序只是執行緒的容器。程式 本身只是指令、資料及

robot framework程式執行過程中遇到點選事件之後未出現點選之後的效果(求解)

1.click Element操作,在實際過程中偶然會出現,日誌顯示已點選成功,但是實際自動化頁面,沒有點選成功之後的操作 現象: 現象描述:程式執行到點選側邊欄的【人員資訊】之後,日誌顯示已經點選成功,但是報錯截圖可以看到並沒有相對應點選成功之後,出現的人員資訊頁面,故判斷找不到開啟頁面 測試中其他的

Nginx 工作原理優化、漏洞

1.  Nginx的模組與工作原理 Nginx由核心和模組組成,其中,核心的設計非常微小和簡潔,完成的工作也非常簡單,僅僅通過查詢配置檔案將客戶端請求對映到一個location block(location是Nginx配置中的一個指令,用於URL匹配),而在這個location中所

jQuery事件處理節點

事件處理: 1、頁面載入後的執行   類似於window.onload 但不同於 window.onload   jQuery載入後執行的特點:   在DOM樹載入完畢的時候就開始執行     $(document).ready( function(){     //頁面的初始化操作     /

JavaScript事件冒泡事件捕獲事件處理事件委託

早期的事件,是作為分擔伺服器運算負載的一種手段,實文件或者瀏覽器視窗中發生的一些特定的互動瞬間,如點選按鈕,拖放檔案等。我們可以使用偵聽器來預定事件,當事件釋出時候就可作出相應的響應,這種模式稱為觀察者模型。 事件流 事件流是從頁面接收事件的順序。在一個html頁面中,

頁面有多個相同的id或者class時繫結jquery事件無效解決方案

<div id="cp_liuyan" class="cp_tl cp_tl2" >按鈕</div> <div id="cp_liuyan" class="cp_tl cp_tl2" >按鈕</div> <div id="

學歷不夠技術來湊程序員摸爬滾打幾年你真搞清自己的方向?

process 電子 迷茫 中學 職業 shadow 要求 軟件 但是 很多所謂的程序員職務,連大學都不需要,只需要中學畢業後去培訓半年一年,就可以混飯吃。 學歷不夠技術來湊,程序員摸爬滾打幾年,你真搞清自己的方向?但是,要當一個獨立的機械技師需要至少3年。當一個成熟的機械

Activity啟動流程介面繪製到事件處理的整個流程(基於Android6.0原始碼)(2)

void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { synchronized (this) { ...... mWindowAttributes

jQuery中的屬性操作jQuery中的事件處理、jQuery 中的動畫簡單介紹

jQuery中的屬性操作,jQuery中的事件處理、jQuery 中的動畫簡單介紹 getAttribute(‘name’) setAttribute(‘name’, ‘Tom’) attr(): 獲取屬性和設定屬性 當為該方法傳遞一個引數時, 即為某元素的獲取指定屬性 當為該方法傳遞兩個引數時, 即為

如何新增HTML元素的事件處理有幾種方法

新增html元素的事件有三種方法。 1.通過HTML元素屬性。簡單說來就是在html結構中,給你要新增事件的元素新增一個屬性。 屬性名為 ‘on’ + 事件名。 如:你要給a元素繫結一個click事件,你就該這麼寫: name 2.通過物件屬性。 物