Nginx本身不會對PHP進行解析,終端對PHP頁面的請求將會被Nginx交給FastCGI進程監聽的IP地址及連接埠,由php-fpm作為動態解析伺服器處理,最後將處理結果再返回給nginx。其實,Nginx就是一個反向 Proxy伺服器。Nginx通過反向 Proxy功能將動態請求轉向後端php-fpm,從而實現對PHP的解析支援,這就是Nginx實現PHP動態解析的原理。
Nginx不支援對外部程式的直接調用或者解析,所有的外部程式(包括PHP)必須通過FastCGI介面來調用。FastCGI介面在Linux下是socket(這個socket可以是檔案socket,也可以是ip socket)。為了調用CGI程式,還需要一個FastCGI的wrapper(wrapper可以理解為用於啟動另一個程式的程式),這個wrapper綁定在某個固定socket上,如連接埠或者檔案socket。當Nginx將CGI請求發送給這個socket的時候,通過FastCGI介面,wrapper接收到請求,然後派生出一個新的線程,這個線程調用解譯器或者外部程式處理指令碼並讀取返回資料;接著,wrapper再將返回的資料通過FastCGI介面,沿著固定的socket傳遞給Nginx;最後,Nginx將返回的資料發送給用戶端。
經典的模型就是Nginx中所用的Master-Worker多進程非同步驅動模型。
父進程建立socket,bind、listen後,通過fork建立多個子進程,每個子進程繼承了父進程的socket,調用accpet開始監聽等待網路連接。這個時候有多個進程同時等待網路的串連事件,當這個事件發生時,這些進程被同時喚醒,就是“驚群”。進程被喚醒,需要進行核心重新調度,這樣每個進程同時去響應這一個事件,而最終只有一個進程能處理事件成功,其他的進程在處理該事件失敗後重新休眠或其他。
其實在Linux2.6版本以後,核心核心已經解決了accept()函數的“驚群”問題,當核心接收到一個客戶串連後,只會喚醒等待隊列上的第一個進程或線程。
Nginx中使用accept_mutexmutex互斥鎖解決這個問題,具體措施有使用全域互斥鎖,每個子進程在epoll_wait()之前先去申請鎖,申請到則繼續處理,擷取不到則等待,並設定了一個負載平衡的演算法(當某一個子進程的任務量達到總設定量的7/8時,則不會再嘗試去申請鎖)來均衡各個進程的任務量。
現在我們對驚群及 Nginx 的處理總結如下:
accept 不會有驚群,epoll_wait 才會。
Nginx 的 accept_mutex,並不是解決 accept 驚群問題,而是解決 epoll_wait 驚群問題。
說Nginx 解決了 epoll_wait 驚群問題,也是不對的,它只是控制是否將監聽通訊端加入到epoll 中。監聽通訊端只在一個子進程的 epoll 中,當新的串連來到時,其他子進程當然不會驚醒了。
簡單了說,就是同一時刻只允許一個nginx worker在自己的epoll中處理監聽控制代碼。它的負載平衡也很簡單,當達到最大connection的7/8時,本worker不會去試圖拿accept鎖,也不會去處理新串連,這樣其他nginx worker進程就更有機會去處理監聽控制代碼,建立新串連了。而且,由於timeout的設定,使得沒有拿到鎖的worker進程,去拿鎖的頻繁更高。
nginx 多進程模型真的沒有鎖了嗎?其實還是有一個的:ngx_accept_mutex。
nginx是一個多進程程式,80連接埠為各worker進程共用,每當有串連到來時,勢必多個worker進程都要爭著去響應,這也就是所謂的驚群現象。
當核心accept一個連結時,會喚醒所有等待中的進程,但實際上只有一個進程能擷取串連,其它的進程都被無效喚醒,這種無效喚醒無疑將會增加應用的開銷。為此,nginx提供了一把accept鎖避免九子奪嫡的悲劇。
ngx_accept_mutex的作用也就是讓那些當前負載嚴重
的worker進程主動放棄對新到來的請求的處理,提高
應用整體的喚醒效率,進而提升應用的整體效能。
proxy_cache
upstream
fastcgi_pass
location
非標準狀態代碼444表示關閉串連且不給用戶端發回應標頭。
nginx -s reload 命令載入修改後的設定檔,命令下達後發生如下事件
1. Nginx的master進程檢查設定檔的正確性,若是錯誤則返回錯誤資訊,nginx繼續採用原設定檔進行工作(因為worker未受到影響)
2. Nginx啟動新的worker進程,採用新的設定檔
3. Nginx將新的請求分配新的worker進程
4. Nginx等待以前的worker進程的全部請求已經都返回後,關閉相關worker進程
5. 重複上面過程,知道全部舊的worker進程都被關閉掉
以上過程是參考nginx官方的相關文檔後得出。
以 proxy_next_upstream為例, 一般的配置如下:
proxy_next_upstream http_504 timeout;
這個指令有兩個作用 :
告訴nginx,如果發生了連線逾時,上遊返回504的時候,需要重試upstream
告訴nginx, http 504 ,連線逾時 是 請求失敗
總的來說, nginx對於 error, timeout, invalide_header 均預設是失敗, 其他的行為如果想當作失敗,需要加入類似 proxy_next_upstream之類的指令裡面的 。 其中 http_403, http_404均不認識是失敗。
論兩點, 對 server 失敗的定義 和 server失敗後的行為。
Ngxin把對用戶端請求的處理過程劃分為11個階段
#1 NGX_HTTP_POST_READ_PHASE: 讀取請求內容階段
#2 NGX_HTTP_SERVER_REWRITE_PHASE: Server請求地址修正階段
#3 NGX_HTTP_FIND_CONFIG_PHASE: 配置尋找階段
#4 NGX_HTTP_REWRITE_PHASE: Location請求地址修正階段
#5 NGX_HTTP_POST_REWRITE_PHASE: 請求地址修正提交階段
#6 NGX_HTTP_PREACCESS_PHASE: 存取權限檢查準備階段
#7 NGX_HTTP_ACCESS_PHASE: 存取權限檢查階段
#8 NGX_HTTP_POST_ACCESS_PHASE: 存取權限檢查提交階段
#9 NGX_HTTP_TRY_FILES_PHASE: 配置項try_files處理階段
#10 NGX_HTTP_CONTENT_PHASE: 內容產生階段
#11 NGX_HTTP_LOG_PHASE: 日誌模組處理階段
Nginx的請求處理流程
#1 Nginx如何確認由哪個server處理該請求?
1. 利用ip + port 確認監聽該ip和連接埠的server。
2. 根據請求中的host首部確認選擇那個server處理該請求。
3. 如果沒有匹配到任何server,則把該請求轉給預設(default)server處理,
一般而言,不加任何設定的話,設定檔中順序出現的第一個server作為
default server。
4. 可以對listen指令使用default_server標誌,去設定某個server為
default server。
#2 Nginx如何根據host首部匹配server?
Nginx主要是通過比較server中的server_name和host首部來匹配server的。
比較順序如下所示:
1. 精確的name;
2. 最長相符的前置萬用字元name(如:*.zhidao.baidu.com);
3. 最長的後導萬用字元name(如:zhidao.baidu.*);
#3 初始化http請求,http請求的11個階段
location匹配命令
~ 波浪線表示執行一個正則匹配,區分大小寫。
~* 表示執行一個正則匹配,不區分大小寫。
^~ ^~表示一般字元匹配,如果該選項匹配,只匹配該選項,不匹配別的選項,一般用來匹配目錄。
= 進行一般字元精確匹配。
@ "@" 定義一個命名的 location,使用在內部定向時,例如 error_page,try_files。
樣本:
請求URI例子:
/ -> 符合configuration A
/documents/document.html -> 符合configuration B
/images/1.gif -> 符合configuration C
/documents/1.jpg ->符合 configuration D