關(guān)閉端口復(fù)用時(shí):在子進(jìn)程分支之前開始監(jiān)聽
打開端口復(fù)用時(shí):在子進(jìn)程分支之后開始監(jiān)聽
這兩種方式有什么區(qū)別呢
還有就是無論哪種,在分支之后相當(dāng)于每個(gè)進(jìn)程都在監(jiān)聽同一個(gè)端口,那數(shù)據(jù)是怎么分發(fā)到一個(gè)進(jìn)程去處理而不是所有進(jìn)程都進(jìn)行處理呢
1、不使用端口復(fù)用(SO_REUSEPORT),要想讓多個(gè)進(jìn)程監(jiān)聽同一個(gè)端口,就需要先讓主進(jìn)程監(jiān)聽端口,然后fork出子進(jìn)程,讓子進(jìn)程繼承主進(jìn)程的監(jiān)聽socket句柄。
這種情況下當(dāng)一個(gè)tcp鏈接到來時(shí),首先tcp鏈接進(jìn)入系統(tǒng)內(nèi)核一個(gè)隊(duì)列里,等待進(jìn)程去接受(accept系統(tǒng)調(diào)用),如果子進(jìn)程都是以io復(fù)用(select/poll/epoll等)的方式監(jiān)聽socket句柄(workerman就是這樣),那么就會(huì)有多個(gè)進(jìn)程被內(nèi)核喚醒同時(shí)去爭(zhēng)搶(accept)這個(gè)隊(duì)列里的tcp鏈接,但是只有一個(gè)進(jìn)程能成功,所以有些進(jìn)程被白白喚醒,造成一些cpu浪費(fèi),這就是所謂的驚群效應(yīng)。
2、打開端口復(fù)用時(shí),主進(jìn)程不創(chuàng)建監(jiān)聽句柄,每個(gè)子進(jìn)程會(huì)自己創(chuàng)建監(jiān)聽句柄(不開端口復(fù)用無法做到這一點(diǎn))
這種情況下當(dāng)一個(gè)tcp鏈接到來時(shí),首先tcp鏈接進(jìn)入系統(tǒng)內(nèi)核一個(gè)隊(duì)列里,這時(shí)候系統(tǒng)內(nèi)核會(huì)自動(dòng)將tcp鏈接分配給某一個(gè)監(jiān)聽句柄的進(jìn)程,這樣只有一個(gè)進(jìn)程被喚醒去接受(accept),所以沒有驚群效應(yīng)。也就是說開啟端口復(fù)用后內(nèi)核會(huì)自動(dòng)做負(fù)載均衡,在短鏈接應(yīng)用中性能會(huì)提升一些(長(zhǎng)鏈接應(yīng)用沒有明顯效果)。
其它相關(guān)鏈接:http://wenda.workerman.net/?/question/179
我編輯Worker.php,在acceptConnection中加入調(diào)試內(nèi)容
$new_socket = @stream_socket_accept($socket, 0, $remote_address);
if (!$new_socket) {
echo 'my pid is '.posix_getpid().', accept failed.'.PHP_EOL;
return;
}
echo 'my pid is '.posix_getpid().', accept success.'.PHP_EOL;
開了4個(gè)子進(jìn)程,然后用客戶端連接端口,發(fā)現(xiàn)每次驚群?jiǎn)酒鸬倪M(jìn)程數(shù)量并不固定呢,這是為什么
my pid is 2388, accept failed.
my pid is 2390, accept failed.
my pid is 2389, accept failed.
my pid is 2387, accept success.
my pid is 2388, accept success.
my pid is 2390, accept failed.
my pid is 2387, accept failed.
my pid is 2390, accept success.
my pid is 2387, accept failed.
my pid is 2389, accept failed.
my pid is 2388, accept failed.
my pid is 2389, accept failed.
my pid is 2387, accept failed.
my pid is 2390, accept success.
my pid is 2390, accept success.
my pid is 2388, accept failed.
my pid is 2390, accept success.
my pid is 2387, accept failed.
my pid is 2390, accept success.
my pid is 2389, accept failed.
my pid is 2387, accept failed.
my pid is 2390, accept success.
my pid is 2388, accept failed.
是的,并不是每次都喚醒所有進(jìn)程。
喚醒數(shù)一般和cpu核數(shù)以及各個(gè)進(jìn)程繁忙程度有關(guān)。
1、如果進(jìn)程都空閑,那么喚醒的進(jìn)程數(shù)一般等于cpu核數(shù)
2、如果有些進(jìn)程正在處理某些請(qǐng)求,這些進(jìn)程就不會(huì)得到io復(fù)用(select/poll/epoll等)關(guān)于有新tcp鏈接的通知,那么喚醒的進(jìn)程數(shù)一般小于cpu核數(shù)。
極端的情況的一個(gè)例子,如果只有一個(gè)進(jìn)程空閑(阻塞在IO復(fù)用系統(tǒng)調(diào)用監(jiān)聽鏈接事件),那么只有它能立刻監(jiān)聽到有鏈接事件,它accept領(lǐng)走鏈接后,系統(tǒng)內(nèi)核鏈接隊(duì)列已經(jīng)是空了,等其它進(jìn)程從繁忙轉(zhuǎn)到空閑時(shí)已經(jīng)沒有鏈接可以accept,所以其它進(jìn)程不會(huì)再被通知有新鏈接而被喚醒。這種情況等于沒發(fā)生驚群效應(yīng)。
我沒研究過linux源碼,上面都是一些經(jīng)驗(yàn)之談,不一定十分準(zhǔn)確。