nginx基本功能和工做原理

nginx能作什麼

  1. 反向代理
  2. 正向代理
  3. 負載均衡
  4. HTTP服務器(包含動靜分離)

反向代理和正向代理

正向代理。簡單的說,我是一個用戶,我沒法直接訪問一個網站,可是我能訪問一個代理服務器,這個代理服務器能訪問那個我不能訪問的網站,因而我先連上代理服務器,告訴它我須要那個沒法訪問網站的內容,代理服務器去取回來,而後返回給我。從網站的角度,只在代理服務器來取內容的時候有一次記錄。結論就是,正向代理,是一個位於客戶端和原始服務器(origin server)之間的服務器,爲了從原始服務器取得內容,客戶端向代理髮送一個請求並指定目標(原始服務器),而後代理向原始服務器轉交請求並將得到的內容返回給客戶端。客戶端必需要進行一些特別的設置才能使用正向代理。nginx

反向代理。例如我要訪問 localhost:8080/views/test1 這個頁面,但view對應的服務器上並無test1這個資源,它是從另外一臺服務器上調用的資源。這樣view對應的那個服務器就使用了反向代理。即用戶只須要把請求發給特定的反向代理服務器,具體請求是誰處理的用戶不須要知道(其實也不知道),由代理服務器統一處理。結論就是 反向代理正好相反,對於客戶端而言它就像是原始服務器,而且客戶端不須要進行任何特別的設置。客戶端向反向代理 的命名空間(name-space)中的內容發送普通請求,接着反向代理將判斷向何處(原始服務器)轉交請求,並將得到的內容返回給客戶端,就像這些內容 本來就是它本身的同樣。web

正向代理的典型用途是爲在防火牆內的局域網客戶端提供訪問Internet的途徑。正向代理還可使用緩衝特性減小網絡使用率。反向代理的典型用途是將 防火牆後面的服務器提供給Internet用戶訪問。反向代理還能夠爲後端的多臺服務器提供負載平衡,或爲後端較慢的服務器提供緩衝服務。算法

總結

正向代理:針對客戶端而言, 代理服務器代理客戶端,轉發請求,並將得到的內容返回給客戶端。 
反向代理:針對客戶端而言, 代理服務器就像是原始服務器,代理集羣的web節點服務器返回結果。apache

負載均衡

負載均衡也是Nginx經常使用的一個功能,負載均衡其意思就是分攤到多個操做單元上進行執行,例如Web服務器、FTP服務器、企業關鍵應用服務器和其它關鍵任務服務器等,從而共同完成工做任務。簡單而言就是當有2臺或以上服務器時,根據規則隨機的將請求分發到指定的服務器上處理,負載均衡配置通常都須要同時配置反向代理,經過反向代理跳轉到負載均衡。而Nginx目前支持自帶3種負載均衡策略,還有2種經常使用的第三方策略。編程

  1. RR 
    按照輪詢(默認)方式進行負載,每一個請求按時間順序逐一分配到不一樣的後端服務器,若是後端服務器down掉,能自動剔除。雖然這種方式簡便、成本低廉。但缺點是:可靠性低和負載分配不均衡。後端

  2. 權重 
    指定輪詢概率,weight和訪問比率成正比,用於後端服務器性能不均的狀況。緩存

    此時8080和8081分別佔90%和10%。upstream test{
         server localhost:8080 weight=9; server localhost:8081 weight=1; }  
  3. ip_hash 
    上面的2種方式都有一個問題,那就是下一個請求來的時候請求可能分發到另一個服務器,當咱們的程序不是無狀態的時候(採用了session保存數據),這時候就有一個很大的很問題了,好比把登陸信息保存到了session中,那麼跳轉到另一臺服務器的時候就須要從新登陸了,因此不少時候咱們須要一個客戶只訪問一個服務器,那麼就須要用iphash了,iphash的每一個請求按訪問ip的hash結果分配,這樣每一個訪客固定訪問一個後端服務器,能夠解決session的問題。bash

    upstream test {
        ip_hash;
        server localhost:8080; server localhost:8081; }
  4. fair(第三方) 
    按後端服務器的響應時間來分配請求,響應時間短的優先分配。服務器

    upstream backend { 
        fair; 
        server localhost:8080; server localhost:8081; }
  5. url_hash(第三方) 
    按訪問url的hash結果來分配請求,使每一個url定向到同一個後端服務器,後端服務器爲緩存時比較有效。 在upstream中加入hash語句,server語句中不能寫入weight等其餘的參數,hash_method是使用的hash算法。markdown

    upstream backend { 
        hash $request_uri; hash_method crc32; server localhost:8080; server localhost:8081; } 

HTTP服務器

Nginx自己也是一個靜態資源的服務器,當只有靜態資源的時候,就可使用Nginx來作服務器,同時如今也很流行動靜分離,就能夠經過Nginx來實現,動靜分離是讓動態網站裏的動態網頁根據必定規則把不變的資源和常常變的資源區分開來,動靜資源作好了拆分之後,咱們就能夠根據靜態資源的特色將其作緩存操做,這就是網站靜態化處理的核心思路。

nginx進程模型

在工做方式上,Nginx分爲單工做進程和多工做進程兩種模式。在單工做進程模式下,除主進程外,還有一個工做進程,工做進程是單線程的;在多工做進程模式下,每一個工做進程包含多個線程。Nginx默認爲單工做進程模式。

Nginx在啓動後,會有一個master進程和多個worker進程。

master進程

主要用來管理worker進程,包含:接收來自外界的信號,向各worker進程發送信號,監控worker進程的運行狀態,當worker進程退出後(異常狀況下),會自動從新啓動新的worker進程。

master進程充當整個進程組與用戶的交互接口,同時對進程進行監護。它不須要處理網絡事件,不負責業務的執行,只會經過管理worker進程來實現重啓服務、平滑升級、更換日誌文件、配置文件實時生效等功能。

咱們要控制nginx,只須要經過kill向master進程發送信號就好了。好比kill -HUP pid,則是告訴nginx,從容地重啓nginx,咱們通常用這個信號來重啓nginx,或從新加載配置,由於是從容地重啓,所以服務是不中斷的。master進程在接收到HUP信號後是怎麼作的呢?

首先master進程在接到信號後,會先從新加載配置文件,而後再啓動新的worker進程,並向全部老的worker進程發送信號,告訴他們能夠光榮退休了。新的worker在啓動後,就開始接收新的請求,而老的worker在收到來自master的信號後,就再也不接收新的請求,而且在當前進程中的全部未處理完的請求處理完成後,再退出。

固然,直接給master進程發送信號,這是比較老的操做方式,nginx在0.8版本以後,引入了一系列命令行參數,來方便咱們管理。好比,./nginx -s reload,就是來重啓nginx,./nginx -s stop,就是來中止nginx的運行。

如何作到的呢?咱們仍是拿reload來講,咱們看到,執行命令時,咱們是啓動一個新的nginx進程,而新的nginx進程在解析到reload參數後,就知道咱們的目的是控制nginx來從新加載配置文件了,它會向master進程發送信號,而後接下來的動做,就和咱們直接向master進程發送信號同樣了。

worker進程

而基本的網絡事件,則是放在worker進程中來處理了。多個worker進程之間是對等的,他們同等競爭來自客戶端的請求,各進程互相之間是獨立的。一個請求,只可能在一個worker進程中處理,一個worker進程,不可能處理其它進程的請求。worker進程的個數是能夠設置的,通常咱們會設置與機器cpu核數一致,這裏面的緣由與nginx的進程模型以及事件處理模型是分不開的。

worker進程之間是平等的,每一個進程,處理請求的機會也是同樣的。當咱們提供80端口的http服務時,一個鏈接請求過來,每一個進程都有可能處理這個鏈接,怎麼作到的呢?首先,每一個worker進程都是從master進程fork過來,在master進程裏面,先創建好須要listen的socket(listenfd)以後,而後再fork出多個worker進程。

全部worker進程的listenfd會在新鏈接到來時變得可讀,爲保證只有一個進程處理該鏈接,全部worker進程在註冊listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個進程註冊listenfd讀事件,在讀事件裏調用accept接受該鏈接。當一個worker進程在accept這個鏈接以後,就開始讀取請求,解析請求,處理請求,產生數據後,再返回給客戶端,最後才斷開鏈接,這樣一個完整的請求就是這樣的了。

咱們能夠看到,一個請求,徹底由worker進程來處理,並且只在一個worker進程中處理。worker進程之間是平等的,每一個進程,處理請求的機會也是同樣的。當咱們提供80端口的http服務時,一個鏈接請求過來,每一個進程都有可能處理這個鏈接,怎麼作到的呢?首先,每一個worker進程都是從master進程fork過來,在master進程裏面,先創建好須要listen的socket(listenfd)以後,而後再fork出多個worker進程。

全部worker進程的listenfd會在新鏈接到來時變得可讀,爲保證只有一個進程處理該鏈接,全部worker進程在註冊listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個進程註冊listenfd讀事件,在讀事件裏調用accept接受該鏈接。當一個worker進程在accept這個鏈接以後,就開始讀取請求,解析請求,處理請求,產生數據後,再返回給客戶端,最後才斷開鏈接,這樣一個完整的請求就是這樣的了。咱們能夠看到,一個請求,徹底由worker進程來處理,並且只在一個worker進程中處理。

多進程IO模型好處

首先,對於每一個worker進程來講,獨立的進程,不須要加鎖,因此省掉了鎖帶來的開銷,同時在編程以及問題查找時,也會方便不少。

其次,採用獨立的進程,可讓互相之間不會影響,一個進程退出後,其它進程還在工做,服務不會中斷,master進程則很快啓動新的worker進程。固然,worker進程的異常退出,確定是程序有bug了,異常退出,會致使當前worker上的全部請求失敗,不過不會影響到全部請求,因此下降了風險。

nginx多進程事件模型:異步非阻塞

雖然nginx採用多worker的方式來處理請求,每一個worker裏面只有一個主線程,那可以處理的併發數頗有限啊,多少個worker就能處理多少個併發,何來高併發呢?非也,這就是nginx的高明之處,nginx採用了異步非阻塞的方式來處理請求,也就是說,nginx是能夠同時處理成千上萬個請求的。

一個worker進程能夠同時處理的請求數只受限於內存大小,並且在架構設計上,不一樣的worker進程之間處理併發請求時幾乎沒有同步鎖的限制,worker進程一般不會進入睡眠狀態,所以,當Nginx上的進程數與CPU核心數相等時(最好每個worker進程都綁定特定的CPU核心),進程間切換的代價是最小的。

而apache的經常使用工做方式(apache也有異步非阻塞版本,但因其與自帶某些模塊衝突,因此不經常使用),每一個進程在一個時刻只處理一個請求,所以,當併發數上到幾千時,就同時有幾千的進程在處理請求了。這對操做系統來講,是個不小的挑戰,進程帶來的內存佔用很是大,進程的上下文切換帶來的cpu開銷很大,天然性能就上不去了,而這些開銷徹底是沒有意義的。

爲何nginx能夠採用異步非阻塞的方式來處理呢,或者異步非阻塞究竟是怎麼回事呢?

咱們先回到原點,看看一個請求的完整過程:首先,請求過來,要創建鏈接,而後再接收數據,接收數據後,再發送數據。

具體到系統底層,就是讀寫事件,而當讀寫事件沒有準備好時,必然不可操做,若是不用非阻塞的方式來調用,那就得阻塞調用了,事件沒有準備好,那就只能等了,等事件準備好了,你再繼續吧。阻塞調用會進入內核等待,cpu就會讓出去給別人用了,對單線程的worker來講,顯然不合適,當網絡事件越多時,你們都在等待呢,cpu空閒下來沒人用,cpu利用率天然上不去了,更別談高併發了。

好吧,你說加進程數,這跟apache的線程模型有什麼區別,注意,別增長無謂的上下文切換。因此,在nginx裏面,最忌諱阻塞的系統調用了。不要阻塞,那就非阻塞嘍。非阻塞就是,事件沒有準備好,立刻返回EAGAIN,告訴你,事件還沒準備好呢,你慌什麼,過會再來吧。

好吧,你過一會,再來檢查一下事件,直到事件準備好了爲止,在這期間,你就能夠先去作其它事情,而後再來看看事件好了沒。雖然不阻塞了,但你得不時地過來檢查一下事件的狀態,你能夠作更多的事情了,但帶來的開銷也是不小的。

 轉載自:https://blog.csdn.net/wy757510722/article/details/75267431
相關文章
相關標籤/搜索