nginx apache 簡單對比
nginx 相對 apache 的優點:
- 輕量級,同樣起web 服務,比apache 占用更少的內存及資源
- 抗並發,nginx 處理請求是異步非阻塞的,而 apache 則是阻塞型的,在高並發下 nginx 能保持低資源低消耗高性能
- 高度模塊化的設計,編寫模塊相對簡單
- 社區活躍
- 配置簡潔
apache 相對nginx 的優點:
- rewrite ,比 nginx 的 rewrite 強大
- 模塊超多
- 少 bug ,nginx 的 bug 相對較多
- 超穩定
- 配置復雜
一般來說,求性能,用 nginx 。求穩定,用 apache 。
初探nginx架構
nginx的高性能與其架構是分不開的。
nginx在啟動后,在類unix系統中會以daemon的方式在后台運行,后台進程包含一個master進程和多個worker進程。
1、master進程主要用來管理worker進程:
1)接收來自外界的信號;
2)向各worker進程發送信號;
3)監控worker進程的運行狀態;
4)當worker進程退出后(異常情況下),會自動重新啟動新的worker進程。
2、worker進程中處理基本的網絡事件:
多個worker進程之間對等,他們同等競爭來自客戶端的請求,各進程之間相互獨立。
一個請求,只可能在一個worker進程中處理。worker進程個數一般我們會設置與機器cpu核數一致。

3、通過信號控制 master 進程:
1)kill -HUP pid,從容地重啟 nginx(不中斷服務)。
master 進程在接到信號后,會先重新加載配置文件,然后再啟動新的 worker 進程,並向所有老的 worker 進程發送信號,告訴他們可以光榮退休了。
新的 worker 在啟動后,就開始接收新的請求,而老的 worker 在收到來自 master 的信號后,就不再接收新的請求,並且在當前進程中的所有未處理完的請求處理完成后,再退出。
2)./nginx -s reload (0.8版本后新的平滑重啟命令)
3)./nginx -s stop,停止 nginx 運行。
4、worker進程工作原理:
每個worker進程都是從master進程fork過來。
在master進程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多個worker進程。所有worker進程的listenfd會在新連接到來時變得可讀。
為保證只有一個進程處理該連接,所有worker進程在注冊listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個進程注冊listenfd讀事件,在讀事件里調用accept接受該連接。
當一個worker進程在accept這個連接之后,就開始讀取請求,解析請求,處理請求,產生數據后,再返回給客戶端,最后才斷開連接,一個完整的請求就是這樣。
所以一個請求,完全由worker進程來處理,而且只在一個worker進程中處理。
5、高並發原理:
nginx采用多worker的方式來處理請求,每個worker里面只有一個主線程,那能夠處理的並發數很有限啊,多少個worker就能處理多少個並發,何來高並發呢?
非也,這就是nginx的高明之處,nginx采用了異步非阻塞的方式來處理請求,也就是說,nginx是可以同時處理成千上萬個請求的。
異步非阻塞的事件處理機制,具體到系統調用就是像select/poll/epoll/kqueue這樣的系統調用。
請求過來,要建立連接,然后再接收數據,接收數據后,再發送數據。具體到系統底層,就是讀寫事件。
拿epoll為例,當事件沒准備好時,放到epoll里面,事件(對於一個基本的web服務器來說,事件通常有三種類型,網絡事件、信號、定時器)准備好了,我們就去讀寫,當讀寫返回EAGAIN時,我們將它再次加入到epoll里面。這樣,只要有事件准備好了,我們就去處理它,只有當所有事件都沒准備好時,才在epoll里面等着。這樣,我們就可以並發處理大量的並發了。
當然,這里的並發請求,是指未處理完的請求,線程只有一個,所以同時能處理的請求當然只有一個了,只是在請求間進行不斷地切換而已,切換也是因為異步事件未准備好,而主動讓出的。這里的切換是沒有任何代價,你可以理解為循環處理多個准備好的事件,事實上就是這樣的。
與多線程相比,這種事件處理方式是有很大的優勢的,不需要創建線程,每個請求占用的內存也很少,沒有上下文切換,事件處理非常的輕量級。並發數再多也不會導致無謂的資源浪費(上下文切換)。更多的並發數,只是會占用更多的內存而已。
nginx基礎概念
connection
在nginx中connection就是對tcp連接的封裝,其中包括連接的socket,讀事件,寫事件。利用nginx封裝的connection,我們可以很方便的使用nginx來處理與連接相關的事情,比如,建立連接,發送與接受數據等。而nginx中的http請求的處理就是建立在connection之上的,所以nginx不僅可以作為一個web服務器,也可以作為郵件服務器。當然,利用nginx提供的connection,我們可以與任何后端服務打交道。
結合一個tcp連接的生命周期,我們看看nginx是如何處理一個連接的。首先,nginx在啟動時,會解析配置文件,得到需要監聽的端口與ip地址,然后在nginx的master進程里面,先初始化好這個監控的socket(創建socket,設置addrreuse等選項,綁定到指定的ip地址端口,再listen),然后再fork出多個子進程出來,然后子進程會競爭accept新的連接。此時,客戶端就可以向nginx發起連接了。當客戶端與服務端通過三次握手建立好一個連接后,nginx的某一個子進程會accept成功,得到這個建立好的連接的socket,然后創建nginx對連接的封裝。接着,設置讀寫事件處理函數並添加讀寫事件來與客戶端進行數據的交換。最后,nginx或客戶端來主動關掉連接,到此,一個連接就壽終正寢了。
request
request,在nginx中我們指的是http請求,包含請求行、請求頭、請求體、響應行、響應頭、響應體。

nginx的配置系統
nginx的配置系統由一個主配置文件和其他一些輔助的配置文件構成。這些配置文件均是純文本文件,全部位於nginx安裝目錄下的conf目錄下。
配置文件中以#開始的行,或者是前面有若干空格或者TAB,然后再跟#的行,都被認為是注釋,也就是只對編輯查看文件的用戶有意義,程序在讀取這些注釋行的時候,其實際的內容是被忽略的。
由於除主配置文件nginx.conf以外的文件都是在某些情況下才使用的,而只有主配置文件是在任何情況下都被使用的。所以在這里我們就以主配置文件為例,來解釋nginx的配置系統。
在nginx.conf中,包含若干配置項。每個配置項由配置指令和指令參數2個部分構成。指令參數也就是配置指令對應的配置值。
指令上下文
nginx.conf中的配置信息,根據其邏輯上的意義,對它們進行了分類,也就是分成了多個作用域,或者稱之為配置指令上下文。不同的作用域含有一個或者多個配置項。
當前nginx支持的幾個指令上下文:
main:
nginx在運行時與具體業務功能(比如http服務或者email服務代理)無關的一些參數,比如工作進程數,運行的身份等。
http:
與提供http服務相關的一些配置參數。例如:是否使用keepalive啊,是否使用gzip進行壓縮等。
server:
http服務上支持若干虛擬主機。每個虛擬主機一個對應的server配置項,配置項里面包含該虛擬主機相關的配置。在提供mail服務的代理時,也可以建立若干server.每個server通過監聽的地址來區分。
location:
http服務中,某些特定的URL對應的一系列配置項。
mail:
實現email相關的SMTP/IMAP/POP3代理時,共享的一些配置項。
存在於main上下文中的配置指令如下:
- user
- worker_processes
- error_log
- events
- http
存在於http上下文中的指令如下:
- server
存在於mail上下文中的指令如下:
- server
- auth_http
- imap_capabilities
存在於server上下文中的配置指令如下:
- listen
- server_name
- access_log
- location
- protocol
- proxy
- smtp_auth
存在於location上下文中的指令如下:
- index
- root
請求的處理流程
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:日志模塊處理階段
nginx location匹配簡介
= 嚴格匹配。如果這個查詢匹配,那么將停止搜索並立即處理此請求。
~ 為區分大小寫匹配(可用正則表達式)
!~為區分大小寫不匹配
~* 為不區分大小寫匹配(可用正則表達式)
!~*為不區分大小寫不匹配
^~ 如果把這個前綴用於一個常規字符串,那么告訴nginx 如果路徑匹配那么不測試正則表達式。
location = / {
# 精確匹配 / ,主機名后面不能帶任何字符串
[ configuration A ]
}
location / {
# 因為所有的地址都以 / 開頭,所以這條規則將匹配到所有請求
# 但是正則和最長字符串會優先匹配
[ configuration B ]
}
location /documents/ {
# 匹配任何以 /documents/ 開頭的地址,匹配符合以后,還要繼續往下搜索
# 只有后面的正則表達式沒有匹配到時,這一條才會采用這一條
[ configuration C ]
}
location ~ /documents/Abc {
# 匹配任何以 /documents/ 開頭的地址,匹配符合以后,還要繼續往下搜索
# 只有后面的正則表達式沒有匹配到時,這一條才會采用這一條
[ configuration CC ]
}
location ^~ /images/ {
# 匹配任何以 /images/ 開頭的地址,匹配符合以后,停止往下搜索正則,采用這一條。
[ configuration D ]
}
location ~* \.(gif|jpg|jpeg)$ {
# 匹配所有以 gif,jpg或jpeg 結尾的請求
# 然而,所有請求 /images/ 下的圖片會被 config D 處理,因為 ^~ 到達不了這一條正則
[ configuration E ]
}
location /images/ {
# 字符匹配到 /images/,繼續往下,會發現 ^~ 存在
[ configuration F ]
}
location /images/abc {
# 最長字符匹配到 /images/abc,繼續往下,會發現 ^~ 存在
# F與G的放置順序是沒有關系的
[ configuration G ]
}
location ~ /images/abc/ {
# 只有去掉 config D 才有效:先最長匹配 config G 開頭的地址,繼續往下搜索,匹配到這一條正則,采用
[ configuration H ]
}
location ~* /js/.*/\.js