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