原創:花括號MC(微信公衆號:huakuohao-mc)。關注JAVA基礎編程及大數據,注重經驗分享及我的成長。linux
爲何httpd的Event模塊比worker更優秀?簡單的說是由於event
模塊採用epoll
方式,而worker
模塊採用select
方式。編程
那麼爲何epoll
就比select
優秀?你們常說的epoll
跟select
又是個什麼東西?tomcat
操做系統分爲用戶空間 跟內核空間。httpd
,tomcat
以及JVM
都是用戶空間的進程。若是想讀取磁盤上的一個文件,大概流程是這個樣子的。用戶空間進程首先發起系統調用,操做系統經過驅動程序操做硬件,將磁盤上的數據加載到內核空間,而後從內核空間複製到用戶空間,這個時候用戶進程就能夠操做數據了。安全
將數據從磁盤或網絡設備複製到內核空間的過程稱爲數據準備階段,將數據從內核空間複製到用戶空間的過程稱爲數據複製階段。數據準備階段通常不須要CPU參與,而數據複製階段是須要CPU參與的。服務器
以下圖所示微信
linux
把一切都看作文件,因此網絡鏈接(socket
)也是文件。對文件的操做須要一個文件描述符(file descriptor);而對文件的操做,本質上就是對I/O
的操做。網絡
I/O
模型大概分爲以下幾種。併發
用戶進程發起系統調用,檢查內核空間是否有數據,沒有數據則從硬盤將數據拷貝到內核空間,這個過程就是上面提到的數據準備階段,而後將內核空間的數據在拷貝到用戶空間中,這個過程是數據複製階段。這個時候用戶進程開始處理數據。 一圖勝千言。異步
阻塞I/O
在數據準備和數據複製兩個階段都被阻塞。這個模型最大的優勢就是簡單。而最大的問題就是處理的文件數變多以後,須要產生大量的進(線)程。Java
的BIO
採用的就是該模型。socket
非阻塞I/O
指的是數據準備階段不會形成阻塞,而是處於不斷的輪詢的狀態,若是沒有數據,則返回EWOULDBLOCK
,有數據以後,進行數據複製操做,數據複製階段進程處於阻塞狀態。 一圖勝千言。
這種模式通常不會單獨使用,可是這種模式成就了下面要介紹的I/O
複用模型。
咳咳,重點來了。操做系統幫咱們實現了非阻塞I/O
模型中,數據準備階段的輪詢操做。內核提供了三個系統調用函數來監控文件是否就緒的狀態,分別是select
, poll
, epoll
。所謂的多路複用就是能夠同時監控多個文件描述符。任意一個文件描述符就緒就開始處理。 一圖勝千言。
用的很少,瞭解一下就好。首先發起一個信號處理的系統調用,如sigaction()
,這個系統調用會當即返回。數據在準備好時,會發送SIGIO信號,進程收到這個信號就知道數據準備好了,因而發起操做數據的系統調用,如read()
。在發起信號處理的系統調用後,進程不會被阻塞,可是在read()
將數據從內核空間複製到用戶空間時,進程是被阻塞的。 一圖勝千言。
當設置爲異步I/O
模型時,首先發起異步系統調用,如aio_read()
,aio_write()
等,並當即返回。這個異步系統調用告訴內核,不只要準備好數據,還要把數據複製到用戶空間中。整個過程都不會被阻塞。 一圖勝千言。
上面說了那麼多I/O
,估計都被我忽悠暈了,一下子阻塞非阻塞,一下子同步異步的。 簡單總結一下,阻塞I/O
、非阻塞I/O
、I/O
複用、信號驅動都是同步I/O
模型。只有異步I/O
模型纔是異步操做。
一圖勝千言。
多路I/O
複用的關鍵優點在於,它能夠一個線程管理多個socket
的狀態。Apache
的httpd
以及Nginx
,還有Redis
都是得意於此種I/O
模型。
給你們舉個小例子,這樣你們理解起來可能就更形象了。 飯店服務員在有顧客進來的時候,把顧客迎進來,而後站在顧客旁邊,等着點菜。點完菜交給廚師。這期間服務員什麼都作不了。這就是阻塞I/O
。若是想同時服務多個顧客就須要多個服務員。服務員至關於進程,進程多了耗費硬件資源不說,管理這些進程的上下文切換也很費事。 若是服務員只負責把顧客迎進來,等顧客點好菜以後,本身告訴服務員,那麼服務員的效率將大大提升。一個服務員就能夠同時服務不少顧客了。這就是I/O
複用模式。就問你厲害不厲害。
select()
函數是最早被Linux
支持的多路複用I/O
函數。可是select()
函數最多隻支持1024
個鏈接,若是監控的鏈接有數據到達,select()
函數只會告訴你有數據準備好了,至於哪些socket
有數據了,須要本身去遍歷。若是鏈接數少還能夠,數據量一大,這酸爽!並且仍是非線程安全。
poll()
函數在1997年被實現,其實跟select()
工做方式相似,主要修改了1024
這個限制。估計你們認爲當時的服務器硬件水平撐1K多個鏈接已經很厲害了,因此就沒有在性能方面作進一步優化,一樣仍是非線成安全的。
epoll()
函數是2002年實現的,也就是說linux2.6
以前的版本用不了。epoll
函數改變的挺多,首先變成線程安全了,一樣也沒有最大鏈接數限制了。並且採用回調函數的方式處理連接,哪一個連接有數據了處理哪一個連接,不再用去輪詢了。這就是Reactor編程。同時epoll()
很好的解決了著名的C10K
問題(即單機支持1萬個併發鏈接問題)。
這裏有一份性能測試對比圖。你們能夠感覺一下。
這就是爲何event
比worker
優秀了。注意event
是在httpd
的2.4版本以後才變得穩定的,以前是實驗版本。同時須要操做系統內核是Linux2.6
以上的版本纔可使用。
·END·
Java·大數據·我的成長