原創:花括號MC(微信公衆號:huakuohao-mc)。關注JAVA基礎編程及大數據,注重經驗分享及我的成長。apache
進行網絡通訊的時候,須要創建一個socket
,這是你們都知道的。若是一個套接字只能被一個監聽進(線)程監聽,那麼豈不是同一時刻httpd
只能處理一個請求,處理完這個請求以後,釋放80
端口在給其餘請求使用。但是顯然httpd
沒有那麼笨,那麼apache httpd
是如何支持高併發的呢?編程
簡單的說就是有兩種套接字,一種是監聽套接字,供監聽進(線)程監聽使用。另外一種是連接套接字,供傳輸數據使用。監聽進(線)程比如麗春院裏面接客的老鴇,而爲客人提供彈唱服務的姑娘至關於worker
模式下的工做線程。老鴇能夠有一個,可是姑娘有不少。在apache httpd
的 worker
模式下,每一個進程會產生多個工做線程及一個監聽線程。監聽線程負責監聽,其餘工做線程負責傳輸數據。問題回答完了,就這麼簡單。服務器
那麼監聽套接字跟鏈接套接字是如何配合工做的呢? 要回答這個問題,須要有兩個背景知識作鋪墊,網絡編程及TCP三次握手。微信
有過網絡編程經驗的同窗都知道,把'大象放冰箱'通常是以下幾個步驟。網絡
socket()
函數創建監聽套接字;bind()
函數綁定剛剛生成的監聽套接字;listen()
函數監聽剛剛生成的監聽套接字;accept()
函數生成鏈接套接字;recv()/send()
函數利用鏈接套接字收發數據;close()
函數斷開鏈接。你們記住這幾個步驟,後面用的到。如今來講TCP的三次握手過程。併發
咱們常常見到的TCP三次握手過程都是這樣的。 以下圖:socket
上面這張圖,只是表面現象。下面我把這張圖沒有體現出來的細節給各位說一說,這對回答上面的問題相當重要。tcp
TCP
的協議棧中維護着兩個隊列。一個是半鏈接隊列,一個是全連接隊列。 客戶端發送SYN
報文到服務端指定端口好比httpd
的默認80
端口;此時客戶端的狀態變爲SYN-SEND
;服務端收到SYN
報文以後,將這個鏈接狀態,存儲到半鏈接隊列中,同時服務端的狀態變動爲SYN-RECV
;而後服務端馬上回一個ACK
報文給客戶端;客戶端收到ACK
以後,回給服務端一個ACK
,服務端收到這個ACK
以後,客戶端跟服務端都變爲ESTABLISHE
狀態;代表TCP三次握手成功。這個時候會把半鏈接隊列中的鏈接,轉移到全連接隊列。全連接隊列中的鏈接都是已經完成三次握手的鏈接。監聽進(線)程監聽監聽套接字,就是工做在上面這個過程的。編輯器
全連接隊列當中的鏈接都是能夠用來直接進行數據傳輸的鏈接了。這個時候可使用accept()
函數,將全連接當中的鏈接取出來,生成鏈接socket
,進行數據傳輸。傳輸完畢以後,通過四次揮手,關掉鏈接,釋放資源。這就是一次完整的TCP傳輸過程。函數
俗話說的好,一圖勝千言。我將上面的過程簡單的畫了一個流程圖出來。
圖中標綠的部分分別是三次握手過程跟四次揮手過程。這兩個階段發生在內核空間。
在
Linux 2.2
之前,listen()
函數有一個backlog
的參數,用於設置這兩個隊列的最大總長度,從Linux 2.2
開始,這個參數只表示全連接隊列的最大長度,而/proc/sys/net/ipv4/tcp_max_syn_backlog
則用於設置半鏈接隊列的最大長度。/proc/sys/net/core/somaxconn
則是硬限制已完成隊列的最大長度,默認爲128
,若是backlog
大於somaxconn
,則backlog
會被強制等於somaxconn
。
apache httpd
的prefork
模式會建立子進程處理http
請求。每一個子進程,即負責監聽又負責處理數據。也就是說從創建三次握手到數據傳輸,再到四次揮手,一個子進程負責到底。若是傳輸的數據量比較大,同時系統高併發有比較高,那就須要創建不少子進程,對服務器的硬件資源是個不小的考驗。
worker
模式是進線程混合模式,主進程會建立子進程,每一個子進程會建立多個工做子線程及一個監聽進程。監聽進程專門負責處理監聽套接字,數據傳輸過程交給其餘工做線程。因此worker
模式要比prefork
模式在性能上提升很多。
event
模式跟worker
模式基本相同,都是進線程混合模式,不一樣的是,該模式還會生成一個專門處理keep-alive
的線程。keep-alive
雖好,可是某些狀況下存在站着茅坑不拉屎的狀況,這樣能夠把保持keep-alive
的空閒鏈接資源進行回收。keep-alive
的問題能夠看我這篇文章。
既然event
和worker
都是進線程混合模式,那麼爲何event
模塊的性能要比worker
模塊的性能好不少呢?我將在下篇博文中詳細說明。
·END·
Java·大數據·我的成長