爲何httpd的event模塊比worker更優秀

原創:花括號MC(微信公衆號:huakuohao-mc)。關注JAVA基礎編程及大數據,注重經驗分享及我的成長。linux

爲何httpd的Event模塊比worker更優秀?簡單的說是由於event模塊採用epoll方式,而worker模塊採用select方式。編程

那麼爲何epoll就比select優秀?你們常說的epollselect又是個什麼東西?tomcat

背景知識1-操做系統

操做系統分爲用戶空間內核空間httpd ,tomcat以及JVM都是用戶空間的進程。若是想讀取磁盤上的一個文件,大概流程是這個樣子的。用戶空間進程首先發起系統調用,操做系統經過驅動程序操做硬件,將磁盤上的數據加載到內核空間,而後從內核空間複製到用戶空間,這個時候用戶進程就能夠操做數據了。安全

將數據從磁盤或網絡設備複製到內核空間的過程稱爲數據準備階段,將數據從內核空間複製到用戶空間的過程稱爲數據複製階段。數據準備階段通常不須要CPU參與,而數據複製階段是須要CPU參與的。服務器

以下圖所示微信

背景知識2-I/O模型

linux把一切都看作文件,因此網絡鏈接(socket)也是文件。對文件的操做須要一個文件描述符(file descriptor);而對文件的操做,本質上就是對I/O的操做。網絡

I/O模型大概分爲以下幾種。併發

阻塞IO(Blocking I/O)

用戶進程發起系統調用,檢查內核空間是否有數據,沒有數據則從硬盤將數據拷貝到內核空間,這個過程就是上面提到的數據準備階段,而後將內核空間的數據在拷貝到用戶空間中,這個過程是數據複製階段。這個時候用戶進程開始處理數據。 一圖勝千言。異步

阻塞I/O數據準備數據複製兩個階段都被阻塞。這個模型最大的優勢就是簡單。而最大的問題就是處理的文件數變多以後,須要產生大量的進(線)程。JavaBIO採用的就是該模型。socket

非阻塞I/O(Non-Blocking I/O)

非阻塞I/O指的是數據準備階段不會形成阻塞,而是處於不斷的輪詢的狀態,若是沒有數據,則返回EWOULDBLOCK,有數據以後,進行數據複製操做,數據複製階段進程處於阻塞狀態。 一圖勝千言。

這種模式通常不會單獨使用,可是這種模式成就了下面要介紹的I/O複用模型。

I/O複用(I/O Multiplexing)

咳咳,重點來了。操做系統幫咱們實現了非阻塞I/O模型中,數據準備階段的輪詢操做。內核提供了三個系統調用函數來監控文件是否就緒的狀態,分別是select, poll, epoll。所謂的多路複用就是能夠同時監控多個文件描述符。任意一個文件描述符就緒就開始處理。 一圖勝千言。

信號驅動式(Signal-Driven IO)

用的很少,瞭解一下就好。首先發起一個信號處理的系統調用,如sigaction(),這個系統調用會當即返回。數據在準備好時,會發送SIGIO信號,進程收到這個信號就知道數據準備好了,因而發起操做數據的系統調用,如read()。在發起信號處理的系統調用後,進程不會被阻塞,可是在read()將數據從內核空間複製到用戶空間時,進程是被阻塞的。 一圖勝千言。

異步I/O(Asynchronous I/O)

當設置爲異步I/O模型時,首先發起異步系統調用,如aio_read()aio_write()等,並當即返回。這個異步系統調用告訴內核,不只要準備好數據,還要把數據複製到用戶空間中。整個過程都不會被阻塞。 一圖勝千言。

I/O總結:

上面說了那麼多I/O,估計都被我忽悠暈了,一下子阻塞非阻塞,一下子同步異步的。 簡單總結一下,阻塞I/O、非阻塞I/OI/O複用、信號驅動都是同步I/O模型。只有異步I/O模型纔是異步操做。

一圖勝千言。

爲何是多路複用IO

多路I/O複用的關鍵優點在於,它能夠一個線程管理多個socket的狀態。Apachehttpd以及Nginx,還有Redis都是得意於此種I/O模型。

給你們舉個小例子,這樣你們理解起來可能就更形象了。 飯店服務員在有顧客進來的時候,把顧客迎進來,而後站在顧客旁邊,等着點菜。點完菜交給廚師。這期間服務員什麼都作不了。這就是阻塞I/O。若是想同時服務多個顧客就須要多個服務員。服務員至關於進程,進程多了耗費硬件資源不說,管理這些進程的上下文切換也很費事。 若是服務員只負責把顧客迎進來,等顧客點好菜以後,本身告訴服務員,那麼服務員的效率將大大提升。一個服務員就能夠同時服務不少顧客了。這就是I/O複用模式。就問你厲害不厲害。

select與epoll

select()函數是最早被Linux支持的多路複用I/O函數。可是select()函數最多隻支持1024個鏈接,若是監控的鏈接有數據到達,select()函數只會告訴你有數據準備好了,至於哪些socket有數據了,須要本身去遍歷。若是鏈接數少還能夠,數據量一大,這酸爽!並且仍是非線程安全。

poll()函數在1997年被實現,其實跟select()工做方式相似,主要修改了1024這個限制。估計你們認爲當時的服務器硬件水平撐1K多個鏈接已經很厲害了,因此就沒有在性能方面作進一步優化,一樣仍是非線成安全的。

epoll()函數是2002年實現的,也就是說linux2.6以前的版本用不了。epoll函數改變的挺多,首先變成線程安全了,一樣也沒有最大鏈接數限制了。並且採用回調函數的方式處理連接,哪一個連接有數據了處理哪一個連接,不再用去輪詢了。這就是Reactor編程。同時epoll()很好的解決了著名的C10K問題(即單機支持1萬個併發鏈接問題)。

這裏有一份性能測試對比圖。你們能夠感覺一下。

結束

這就是爲何eventworker優秀了。注意event是在httpd的2.4版本以後才變得穩定的,以前是實驗版本。同時須要操做系統內核是Linux2.6以上的版本纔可使用。

·END·
 

花括號MC

Java·大數據·我的成長

微信號:huakuohao-mc
相關文章
相關標籤/搜索