對於 Nginx,相信有過 Web 服務部署經驗的同學都不陌生,它有以下特點:
- 是一個高性能的 HTTP 和反向代理服務器,也是一個 IMAP/POP3/SMTP 代理服務器。
- Nginx 相較於 Apache 具有佔有內存少,穩定性高等優勢,並且依靠併發能力強,豐富的模塊庫以及友好靈活的配置而聞名。
Nginx 目前部署量逐漸增加,大多數運維人員多多少少都懂點 Nginx,但是真正其明白原理的可能少之又少,在許多面試中可能就 Nginx 會涉及一些實現層面的問題。比如一道阿里的面試題是:說說看 Nginx 所使用的 epoll 模型是什麼?
錯誤回答:Nginx 相比較其他服務器來說就是快,高併發,快速響應,因為用了 epoll......
所以 epoll 以及一般可以同時見到的 select 或者 poll 分別是什麼呢?
三個都是 IO 多路複用的機制,可以監視多個描述符的讀 / 寫等事件,一旦某個描述符就緒(一般是讀或者寫事件發生了),就能夠將發生的事件通知給關心的應用程序去處理該事件。
一些 Linux 知識鋪墊
在實際開始前,我們先回顧一點 Linux 的知識,對於 Linux 而言:
一切都是文件
然而為了區分不同類型的事物,我們有了:
- 普通文件
- 目錄文件
- 鏈接文件
- 設備文件
其中文件描述符(file descriptor)是內核為了高效管理已被打開的文件所創建的索引,其值是一個非負整數(通常是小整數),用於指代被打開的文件,所有執行 I/O 操作的系統調用都通過文件描述符。
如果直接這麼講可能有些難以理解,對於 Linux 有一些使用的用戶來說,會有類似如下的寫法:
g++ lots_of_errors 2>&1 | head
其中 2>&1中的 2 就是表示的「標準錯誤」,1 就是「標準輸出」,中間的 & 表示後面跟的數字是文件描述符而不是一個文件(不然所有的「標準錯誤」就都重定向到了一個名為 1 的文件中了)。
有了上面的知識,我們就可以開始來探索 select,poll 和 epoll 分別是什麼了~
多路複用
如文初的說明表示,這三者都是 I/O 多路複用機制,且簡要介紹了多路複用的定義,那麼如何更加直觀地瞭解多路複用呢?
這裡有張圖:
對於網頁服務器 Nginx 來說,會有很多連接進來, epoll 會把他們都監視起來,然後像撥開關一樣,誰有數據就撥向誰,然後調用相應的代碼處理。
一般來說以下場合需要使用 I/O 多路複用:
- 當客戶處理多個描述字時(一般是交互式輸入和網絡套接口)
- 如果一個服務器既要處理 TCP,又要處理 UDP,一般要使用 I/O 複用
- 如果一個 TCP 服務器既要處理監聽套接口,又要處理已連接套接口
select (1983)
對應的頭文件和函數原型為:
I/O 多路複用這個概念被提出來以後, select 是第一個實現,一個 select 的調用過程圖如下所示:
其缺點為:
- 每次調用 select,都需要把 fd 集合從用戶態拷貝到內核態,這個開銷在 fd 很多時會很大
- 同時每次調用 select 都需要在內核遍歷傳遞進來的所有 fd,這個開銷在 fd 很多時也很大
- select 支持的文件描述符數量只有 1024,非常小
如果系統支持的文件描述符數量不夠,在 Linux 上一般就會表現為:
Too many open files (24)
此時就需要通過類似:ulimit -n 2048 的方式來臨時提升。
poll (1997)
對應的頭文件和函數原型為:
poll 和 select 原理一樣,不過相比較 select 而言,poll 可以支持大於 1024 個文件描述符。
epoll (2002)
對應的頭文件和函數原型為:
相比較 select 和 poll,epoll 的最大特點是:
- epoll 現在是線程安全的,而 select 和 poll 不是。
- epoll 內部使用了 mmap 共享了用戶和內核的部分空間,避免了數據的來回拷貝。
- epoll 基於事件驅動,epoll_ctl 註冊事件並註冊 callback 回調函數,epoll_wait 只返回發生的事件避免了像 select 和 poll 對事件的整個輪尋操作。
什麼是回調?一個簡單的例子:
- 四六級考試成績快要出來的那段時間,小張每隔一段時間就去嘗試查一下成績,這個被稱為輪訓。
- 小張並不在意瘋狂刷新頁面的事情,等到四六級成績出來之後他的手機會自動收到考試院推送的一個小時:「叮,你的六級沒過」,這樣就是回調。
另一個方便理解的對比如下:
- 對於 select / poll 模型來說,可以理解為讓酒店代理訂票,然後每隔幾個小時就問一下買到沒有,酒店在第二天訂到了票,交錢給酒店拿到票,這樣會需要額外的打電話時間和精力。
- 對於 epoll 來說則是委託酒店幫忙訂票,但是並不反覆去問,酒店在第二天買到了票,酒店打電話通知來領票,交錢給酒店拿到票。
epoll 和 Nginx
回到文章開頭,最後我們可以簡單總結一下為什麼有了 epoll 的 Nginx 會有很高的運行效率,其原因在於它使用了異步,非阻塞,IO 多路複用。但是我們是不是就應該吹爆 Nginx,表示「Nginx 完爆 Apache」呢?
其實不是,相比較 Nginx 而言,Apache 作為一個非常老牌的網頁服務器,其有豐富的模塊組件支持,穩定性強,BUG 少,動態內容處理強,而 Nginx 的優勢主要則在於佔用資源少,負載均衡,高併發處理強,靜態內容處理高效,所有隻有掌握了自己的具體業務場景,才可以分情況地討論這兩個服務器之間的區別。
對於 Nginx 而言,其實還有許多高頻面試題,例如:
- Nginx 常用命令有哪些?
需要熟悉:nginx -t ,nginx -s stop 之類
- Nginx 返回 502 錯誤的可能原因?
這個得分情況分類討論,一般可能是後端服務器掛了,也有可能是 Proxy Buffer 不夠
- 正向代理和反向代理之間的區別是什麼?
正向代理:代理端代理的是客戶端反向代理:代理端代理的是服務端
- 什麼是負載均衡?
代理服務器將接收的請求均衡的分發到各服務器
閱讀更多 力扣LeetCode 的文章