Nginx簡介

Nginx是什麼

沒有聽過Nginx?那麼必定聽過它的「同行」Apache吧!Nginx同Apache同樣都是一種WEB服務器。基於REST架構風格,以統一資源描述符(Uniform Resources Identifier)URI或者統一資源定位符(Uniform Resources Locator)URL做爲溝通依據,經過HTTP協議提供各類網絡服務。php

然而,這些服務器在設計之初受到當時環境的侷限,例如當時的用戶規模,網絡帶寬,產品特色等侷限而且各自的定位和發展都不盡相同。這也使得各個WEB服務器有着各自鮮明的特色。html

Apache的發展時期很長,並且是毫無爭議的世界第一大服務器。它有着不少有點:穩定、開源、跨平臺等等。可是因爲它出現的時間太長了。它興起的年代,互聯網產業遠比不上如今。因此它被設計爲一個重量級的。不支持高併發的服務器。在Apache上運行數以萬計的併發訪問,會致使服務器消耗大量內存。操做系統對其進行進程或線程間的切換也消耗了大量的CPU資源,致使HTTP請求的平均響應速度下降。nginx

這些都決定了Apache不可能成爲高性能WEB服務器,輕量級高併發服務器Nginx和Lighttpd就應運而生了。web

 

Nginx產生

又是拜大神的時候了,此次被選中的人是俄羅斯的工程師Igor Sysoev,他在爲Rambler Media工做期間,使用C語言開發了Nginx。Nginx做爲WEB服務器一直爲Rambler Media提供出色而又穩定的服務。apache

而後呢,Igor Sysoev將Nginx代碼開源,而且賦予自由軟件許可證。編程

因爲:ubuntu

  • Nginx使用基於事件驅動架構,使得其能夠支持數以百萬級別的TCP鏈接
  • 高度的模塊化和自由軟件許可證是的第三方模塊層出不窮(這是個開源的時代啊~)
  • Nginx是一個跨平臺服務器,能夠運行在Linux, FreeBSD, Solaris, AIX, Mac OS, Windows等操做系統上
  • 這些優秀的設計帶來的極大的穩定性。

因而,duang的一下。Nginx火了。vim

 

Nginx基本概念

靜態HTTP服務器

首先,Nginx是一個HTTP服務器,能夠將服務器上的靜態文件(如HTML、圖片)經過HTTP協議展示給客戶端。
配置:服務器

server {
    listen 80; # 端口號
    location / {
        root /usr/share/nginx/html; # 靜態文件路徑
    }
}

反向代理服務器

什麼是反向代理?網絡

客戶端原本能夠直接經過HTTP協議訪問某網站應用服務器,若是網站管理員在中間加上一個Nginx,客戶端請求Nginx,Nginx請求應用服務器,而後將結果返回給客戶端,此時Nginx就是反向代理服務器。

反向代理配置:

server {
    listen 80;
    location / {
        proxy_pass http://192.168.0.112:8080; # 應用服務器HTTP地址
    }
}

既然服務器能夠直接HTTP訪問,爲何要在中間加上一個反向代理,不是畫蛇添足嗎?反向代理有什麼做用?繼續往下看,下面的負載均衡、虛擬主機,都基於反向代理實現,固然反向代理的功能也不只僅是這些。

負載均衡

當網站訪問量很是大,也攤上事兒了。由於網站愈來愈慢,一臺服務器已經不夠用了。因而將相同的應用部署在多臺服務器上,將大量用戶的請求分配給多臺機器處理。同時帶來的好處是,其中一臺服務器萬一掛了,只要還有其餘服務器正常運行,就不會影響用戶使用。
Nginx能夠經過反向代理來實現負載均衡。


 
負載均衡配置:
  vim /etc/nginx/nginx.conf 配置文件http內
    upstream myapp {
    server 39.106.191.226:8000; # 應用服務器1
    server 39.97.119.81:8001; # 應用服務器2
}
在 vim /etc/nginx/conf.d/default.conf 服務配置呢把設置好的應用服務器
server {
    listen 80;
    location / {
        proxy_pass myweb;
    }
}

虛擬主機

有的網站訪問量大,須要負載均衡。然而並非全部網站都如此出色,有的網站,因爲訪問量過小,須要節省成本,將多個網站部署在同一臺服務器上。

例如將www.aaa.comwww.bbb.com兩個網站部署在同一臺服務器上,兩個域名解析到同一個IP地址,可是用戶經過兩個域名卻能夠打開兩個徹底不一樣的網站,互相不影響,就像訪問兩個服務器同樣,因此叫兩個虛擬主機。

配置:
server {
    listen 80 default_server;
    server_name _;
    return 444; # 過濾其餘域名的請求,返回444狀態碼
}
server {
    listen 80;
    server_name www.aaa.com; # www.aaa.com域名
    location / {
        proxy_pass http://localhost:8080; # 對應端口號8080
    }
}
server {
    listen 80;
    server_name www.bbb.com; # www.bbb.com域名
    location / {
        proxy_pass http://localhost:8081; # 對應端口號8081
    }
}

在服務器8080和8081分別開了一個應用,客戶端經過不一樣的域名訪問,根據server_name能夠反向代理到對應的應用服務器。

虛擬主機的原理是經過HTTP請求頭中的Host是否匹配server_name來實現的,有興趣的同窗能夠研究一下HTTP協議。

另外,server_name配置還能夠過濾有人惡意將某些域名指向你的主機服務器。

 

FastCGI

Nginx自己不支持PHP等語言,可是它能夠經過FastCGI來將請求扔給某些語言或框架處理(例如PHP、Python、Perl)。

server {
    listen 80;
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /PHP文件路徑$fastcgi_script_name; # PHP文件路徑
        fastcgi_pass 127.0.0.1:9000; # PHP-FPM地址和端口號
        # 另外一種方式:fastcgi_pass unix:/var/run/php5-fpm.sock;
    }
}

配置中將.php結尾的請求經過FashCGI交給PHP-FPM處理,PHP-FPM是PHP的一個FastCGI管理器。有關FashCGI能夠查閱其餘資料,本篇再也不介紹。

 

fastcgi_pass和proxy_pass有什麼區別?
下面一張圖帶你看明白:
 

Nginx經常使用命令

1. 啓動 Nginx

poechant@ubuntu:sudo ./sbin/nginx

2. 中止 Nginx

poechant@ubuntu:sudo ./sbin/nginx -s stoppoechant@ubuntu:sudo ./sbin/nginx -s quit

-s都是採用向 Nginx 發送信號的方式。

3. Nginx 重載配置

poechant@ubuntu:sudo ./sbin/nginx -s reload

上述是採用向 Nginx 發送信號的方式,或者使用:

poechant@ubuntu:service nginx reload

4. 指定配置文件

poechant@ubuntu:sudo ./sbin/nginx -c /usr/local/nginx/conf/nginx.conf

-c表示configuration,指定配置文件。

5. 查看 Nginx 版本

有兩種能夠查看 Nginx 的版本信息的參數。第一種以下:

poechant@ubuntu:/usr/local/nginx$ ./sbin/nginx -v
nginx: nginx version: nginx/1.0.0

另外一種顯示的是詳細的版本信息:

poechant@ubuntu:/usr/local/nginx$ ./sbin/nginx -V
nginx: nginx version: nginx/1.0.0
nginx: built by gcc 4.3.3 (Ubuntu 4.3.3-5ubuntu4) 
nginx: TLS SNI support enabled
nginx: configure arguments: --with-http_ssl_module --with-openssl=/home/luming/openssl-1.0.0d/

6. 檢查配置文件是否正確

poechant@ubuntu:/usr/local/nginx$ ./sbin/nginx -t
nginx: [alert] could not open error log file: open() "/usr/local/nginx/logs/error.log" failed (13: Permission denied)
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
2012/01/09 16:45:09 [emerg] 23898#0: open() "/usr/local/nginx/logs/nginx.pid" failed (13: Permission denied)
nginx: configuration file /usr/local/nginx/conf/nginx.conf test failed

若是出現如上的提示信息,表示沒有訪問錯誤日誌文件和進程,能夠sudo(super user do)一下:

poerchant@ubuntu:/usr/local/nginx$ sudo ./sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

若是顯示如上,則表示配置文件正確。不然,會有相關提示。

7. 顯示幫助信息

poechant@ubuntu:/user/local/nginx$ ./sbin/nginx -h

或者:

poechant@ubuntu:/user/local/nginx$ ./sbin/nginx -?

以上這些涵蓋了 Nginx 平常維護的全部基本操做,另外還有向 master 進程發送信號的相關命令,咱們會在後續看到。

初探nginx架構

進程模型

衆所周知,nginx性能高,而nginx的高性能與其架構是分不開的。那麼nginx到底是怎麼樣的呢?這一節咱們先來初識一下nginx框架吧。

nginx在啓動後,在unix系統中會以daemon的方式在後臺運行,後臺進程包含一個master進程和多個worker進程。咱們也能夠手動地關掉後臺模式,讓nginx在前臺運行,而且經過配置讓nginx取消master進程,從而可使nginx以單進程方式運行。

很顯然,生產環境下咱們確定不會這麼作,因此關閉後臺模式,通常是用來調試用的,在後面的章節裏面,咱們會詳細地講解如何調試nginx。因此,咱們能夠看到,nginx是以多進程的方式來工做的,固然nginx也是支持多線程的方式的,只是咱們主流的方式仍是多進程的方式,也是nginx的默認方式。nginx採用多進程的方式有諸多好處,因此我就主要講解nginx的多進程模式吧。

剛纔講到,nginx在啓動後,會有一個master進程和多個worker進程。

master進程主要用來管理worker進程,包含:接收來自外界的信號,向各worker進程發送信號,監控worker進程的運行狀態,當worker進程退出後(異常狀況下),會自動從新啓動新的worker進程。而基本的網絡事件,則是放在worker進程中來處理了。多個worker進程之間是對等的,他們同等競爭來自客戶端的請求,各進程互相之間是獨立的。一個請求,只可能在一個worker進程中處理,一個worker進程,不可能處理其它進程的請求。worker進程的個數是能夠設置的,通常咱們會設置與機器cpu核數一致,這裏面的緣由與nginx的進程模型以及事件處理模型是分不開的。nginx的進程模型,能夠由下圖來表示:

nginx進程操做

在nginx啓動後,若是咱們要操做nginx,要怎麼作呢?

從上文中咱們能夠看到,master來管理worker進程,因此咱們只須要與master進程通訊就好了。master進程會接收來自外界發來的信號,再根據信號作不一樣的事情。因此咱們要控制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進程發送信號同樣了。

事件模型

如今,咱們知道了當咱們在操做nginx的時候,nginx內部作了些什麼事情,那麼,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進程中處理。

那麼,nginx採用這種進程模型有什麼好處呢?固然,好處確定會不少了。首先,對於每一個worker進程來講,獨立的進程,不須要加鎖,因此省掉了鎖帶來的開銷,同時在編程以及問題查找時,也會方便不少。其次,採用獨立的進程,可讓互相之間不會影響,一個進程退出後,其它進程還在工做,服務不會中斷,master進程則很快啓動新的worker進程。固然,worker進程的異常退出,確定是程序有bug了,異常退出,會致使當前worker上的全部請求失敗,不過不會影響到全部請求,因此下降了風險。固然,好處還有不少,你們能夠慢慢體會。

nginx事件處理

上面講了不少關於nginx的進程模型,接下來,咱們來看看nginx是如何處理事件的。

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

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

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

咱們先回到原點,看看一個請求的完整過程。首先,請求過來,要創建鏈接,而後再接收數據,接收數據後,再發送數據。具體到系統底層,就是讀寫事件,而當讀寫事件沒有準備好時,必然不可操做,若是不用非阻塞的方式來調用,那就得阻塞調用了,事件沒有準備好,那就只能等了,等事件準備好了,你再繼續吧。

阻塞調用會進入內核等待,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的指令數等等。

 
 
 

https://www.jianshu.com/p/630e2e1ca57f?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends

相關文章
相關標籤/搜索