Workerman的reusePort屬性詳解

2021-08-15 22:21:36 字數 3096 閱讀 5172

workerman是乙個高效能的php socket伺服器框架。可以用 workerman 直接在 tcp 層程式設計,基本的程式設計套路是:

$w = new workerman\worker('tcp:');

$w->count = 4;

$w->onmessage = function

(workerman\connection\tcpconnection $connection, array $data) ;

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()

...}

listen()函式的作用就是在當前程序建立乙個 socket 並開始監聽請求。

當 reuseport 為false時,主程序在建立 worker 之前就呼叫了listen()函式:

protected

function

initworkers

() ....

}

隨後主程序通過pcntl_fork()建立 worker。pcntl_fork()有個特性:建立出來的子程序(worker)中的變數都是父程序複製而來的,包括父程序建立的 ma

inso

cket

。所以,

當reu

sepo

rt為∗

∗fal

se∗∗

時,所有

的wor

ker都

複製父進

程的m ai

nsoc

ket。

所以,當

reus

epor

t為∗∗

fals

e∗∗時

,所有的

work

er都復

制父程序

的_mainsocket,也即共用乙個 socket。

而當 reuseport 為true時,情況就不同了。主程序在建立 worker 前不會呼叫listen(),而是在建立完 worker 後由每個 worker 自行發起listen()呼叫:

protected

static

function

forkoneworkerforlinux

($worker)

...}

...}

這樣的結果就是,每個子程序(worker)都建立了自己的 socket。

最後還有一點,如果想要核心開啟 reuseport 功能,需要手動設定 socket 的 context:

if ($this->reuseport)
[1]

[2]

關於埠reuse的問題

最近在除錯php程式的時候發現了乙個很鬱悶的問題 自己寫的程式是將大概22w條資料post到指定介面 程式在伺服器a執行得好好的,但是一放到伺服器b就會出現post失敗的現象 22w條大概會失敗3w多條,而且是間斷並且隨機的post失敗 我用的是curl庫,curl exec後返回false 後面經...

TIME WAIT狀態和reuse問題

time wait狀態和reuse問題。上一篇看了tcp的三次握手與四次揮手,記得四次揮手,主動斷開連線的一方最後乙個狀態就是time wait狀態,並且一定是主動斷開連線的一方,它可能使socket能陷入一種時間比較長的狀態,過多的time wait會影響新socket的建立。那麼time wai...

workerman啟動失敗

啟動後報錯類似如下 php start.php start php warning stream socket server unable to connect to tcp address already in use in home workerman chat workerman worker...