您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Workerman中reusePort屬性的作用是什么,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對相關(guān)知識(shí)有一定的了解。
Workerman是一個(gè)高性能的PHP Socket服務(wù)器框架。可以用 Workerman 直接在 TCP 層編程,基本的編程套路是:
$w = new Workerman\Worker('tcp://0.0.0.0:80'); $w->count = 4; $w->onMessage = function(Workerman\COnnection\TcpConnection $connection, array $data) { $connection->send('Hello World'); }; Worker::runAll();
在使用的過程中,不知道你是否留意過 reusePort 這個(gè)參數(shù),他默認(rèn)被設(shè)置為 false。這個(gè)參數(shù)有什么用?什么情況下我們需要把他設(shè)置為 true,從而提高性能呢?
關(guān)于 reusePort 參數(shù),Workerman官方的文檔是這么解釋的:
開啟監(jiān)聽端口復(fù)用后允許多個(gè)無親緣關(guān)系的進(jìn)程監(jiān)聽相同的端口,并且由系統(tǒng)內(nèi)核做負(fù)載均衡,決定將socket連接交給哪個(gè)進(jìn)程處理,避免了驚群效應(yīng),可以提升多進(jìn)程短連接應(yīng)用的性能。
如果沒有深入研究過 Linux 網(wǎng)絡(luò)編程,很難理解這句話。在此簡單解釋一下:
服務(wù)端程序通常通過監(jiān)聽服務(wù)器上的某個(gè)端口號(hào),來接收客戶端的請求。在Linux中,服務(wù)器網(wǎng)卡 + 端口號(hào)被抽象成了一個(gè) Socket 。
為了提升性能,一般的服務(wù)端程序在運(yùn)行時(shí)都有多個(gè)進(jìn)程(俗稱 Worker)監(jiān)聽同一個(gè) Socket,在沒有客戶端連接到來的時(shí)候,這些Worker是處于掛起狀態(tài)的,不消耗CPU資源。
如果某一刻有一個(gè)客戶端連接到來,Linux 內(nèi)核就會(huì)同時(shí)喚醒這些 Worker,讓他們競爭去處理這個(gè)連接,
結(jié)果只有一個(gè) Worker 可以獲得處理這個(gè)連接的機(jī)會(huì),其他Worker在競爭失敗后繼續(xù)回到掛起狀態(tài)。喚醒 Worker 的過程是要消耗CPU資源的,Worker 數(shù)量越多,消耗的 CPU 資源就越多,造成了資源的浪費(fèi)。這就是常說的 驚群效應(yīng)。
你也許會(huì)問:為什么不每次只喚醒一個(gè)Worker呢?很遺憾,Linux內(nèi)核并沒有這樣的功能。
幸好,在 Linux 3.9 及以后的版本,加入 reuseport 特性。這個(gè)特性有什么用呢?
在有 reuseport 之前,一個(gè)端口號(hào)只能被一個(gè) Socket 監(jiān)聽,有了 reuseport 之后,這個(gè)限制就被打破了:一個(gè)端口號(hào)可以被多個(gè) Socket 同時(shí)監(jiān)聽。
前面說到,Linux 內(nèi)核沒法做到一次只喚醒一個(gè) Worker,但是,內(nèi)核可以做到將客戶端連接均勻地發(fā)送到監(jiān)聽統(tǒng)一端口的一群 Socket 上。
如圖所示,每個(gè) Worker 都有自己的 Socket,都監(jiān)聽同一個(gè)端口。當(dāng)有客戶端連接到來時(shí),內(nèi)核轉(zhuǎn)發(fā)連接到一個(gè) Socket 上,而這個(gè) Socket 只會(huì)喚醒自己隸屬的那個(gè) Worker。這樣就很巧妙地解決了 驚群效應(yīng),提高了整體的性能。
由此,我們可以得出結(jié)論:如果你的 Linux 內(nèi)核版本是 3.9 及以上的話,那么在使用 Workerman 時(shí),可以將 reusePort 設(shè)置為 true 提升程序運(yùn)行效率。
雖然你只要在 Workerman 中把 reusePort 設(shè)置為 true,就能享受到 Linux 的這個(gè)高級特性。但 Workerman 的源碼中,并不只是開啟一個(gè)內(nèi)核參數(shù)那么簡單。Workerman 為你隱藏了許多的設(shè)計(jì)細(xì)節(jié),我們來研究下。
Worker
類是 Workerman 里最主要的類,其中有個(gè) listen()
函數(shù):
protected function listen() { ... if (!$this->_mainSocket) { ... $this->_mainSocket = stream_socket_server(...); ... } ... }
listen()
函數(shù)的作用就是在當(dāng)前進(jìn)程創(chuàng)建一個(gè) Socket 并開始監(jiān)聽請求。
當(dāng) reusePort 為 false 時(shí),主進(jìn)程在創(chuàng)建 Worker 之前就調(diào)用了 listen()
函數(shù):
protected function initWorkers() { .... if (!$worker->reusePort) { $worker->listen(); } .... }
隨后主進(jìn)程通過 pcntl_fork() 創(chuàng)建 Worker。pcntl_fork() 有個(gè)特性:創(chuàng)建出來的子進(jìn)程(Worker)中的變量都是父進(jìn)程復(fù)制而來的,包括父進(jìn)程創(chuàng)建的 mainSocket。所以,當(dāng)reusePort為??false??時(shí),所有的Worker都復(fù)制父進(jìn)程的mainSocket。所以,當(dāng)reusePort為??false??時(shí),所有的Worker都復(fù)制父進(jìn)程的_mainSocket,也即共用一個(gè) Socket。
而當(dāng) reusePort 為 true 時(shí),情況就不同了。主進(jìn)程在創(chuàng)建 Worker 前不會(huì)調(diào)用 listen()
,而是在創(chuàng)建完 Worker 后由每個(gè) Worker 自行發(fā)起 listen()
調(diào)用:
protected static function forkOneWorkerForLinux($worker) { ... $pid = pcntl_fork(); if ($pid === 0) { if ($worker->reusePort) { $worker->listen(); } ... } ... }
這樣的結(jié)果就是,每個(gè)子進(jìn)程(Worker)都創(chuàng)建了自己的 Socket。
最后還有一點(diǎn),如果想要內(nèi)核開啟 reuseport 功能,需要手動(dòng)設(shè)置 Socket 的 context:
if ($this->reusePort) { $context = stream_context_create(); stream_context_set_option($context, 'socket', 'so_reuseport', 1); }
關(guān)于Workerman中reusePort屬性的作用是什么就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。