首頁  >  文章  >  php框架  >  Workerman中你不得不知道的屬性reusePort

Workerman中你不得不知道的屬性reusePort

醉折花枝作酒筹
醉折花枝作酒筹原創
2021-07-23 16:01:352999瀏覽

Workerman是純PHP開發的開源高效能的非同步PHP socket框架。支援TCP長連接,支援websocket、MQTT等諸多協定。今天我們來介紹Workerman中的reusePort屬性,有需要的可以參考參考。

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,從而提高效能呢?

1. reuseport 的作用

關於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 提升程式運作效率。

2. Workerman 如何利用 reuseport

雖然你只要在 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);
}

推荐学习:php视频教程

以上是Workerman中你不得不知道的屬性reusePort的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn