日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

【Swoole系列3.2】Swoole 異步進(jìn)程服務(wù)系統(tǒng)

 硬核項(xiàng)目經(jīng)理 2022-07-28 發(fā)布于湖南

Swoole 異步進(jìn)程服務(wù)系統(tǒng)

在了解了整個(gè)進(jìn)程、線程、協(xié)程相關(guān)的知識(shí)后,我們?cè)賮砜纯丛?Swoole 中是如何通過異步方式處理進(jìn)程問題的,并且了解一下線程在 Swoole 中的作用。

Server兩種運(yùn)行模式

其實(shí)在之前的測(cè)試代碼中,我們就已經(jīng)見到過這兩種模式了,只是當(dāng)時(shí)沒說而已。不管是 Http 還是 TCP 等服務(wù)中,我們都有第三個(gè)參數(shù)的存在,默認(rèn)情況下,它會(huì)賦值為一個(gè) SWOOLE_PROCESS 參數(shù),因此,如果是默認(rèn)情況下我們一般不會(huì)寫這個(gè)參數(shù)。而另外一種模式就是 SWOOLE_BASE 。

SWOOLE_BASE模式

這種模式就是傳統(tǒng)的異步非阻塞模式,它的效果和 Nginx 以及 Node.js 是完全一樣的。在 Node.js 中,是通過一個(gè)主線線程來處理所有的請(qǐng)求,然后對(duì) I/O 操作進(jìn)行異步線程處理,避免創(chuàng)建、銷毀線程以及線程切換的消耗。當(dāng) I/O 任務(wù)完成后,通過觀察者執(zhí)行指定的回調(diào)函數(shù),并把這個(gè)完成的事件放到事件隊(duì)列的尾部,等待事件循環(huán)。

這個(gè)東西吧,要講清楚,開一個(gè)大系列都不為過。但是如果你之前學(xué)習(xí)過一點(diǎn) Node 的話,那么其實(shí)就很好理解。因?yàn)槲覀冎皩懙母鞣N Server 服務(wù)代碼,其實(shí)和 Node 中寫得基本完全一樣。都是一個(gè)事件,然后監(jiān)聽成功后在回調(diào)函數(shù)中寫業(yè)務(wù)代碼。這就是通過回調(diào)機(jī)制來實(shí)現(xiàn)的異步非阻塞模式,將耗時(shí)操作放在回調(diào)函數(shù)中。有興趣的同學(xué)可以去簡(jiǎn)單地學(xué)習(xí)一下 Node.js ,只要有 JS 基礎(chǔ),一兩看看完一套入門教程就可以了。

在 Swoole 的 SWOOLE_BASE 模式下,原理也是完全一樣的。當(dāng)一個(gè)請(qǐng)求進(jìn)來的時(shí)候,所有的 Worker 都會(huì)爭(zhēng)搶這一個(gè)連接,并最終會(huì)有一個(gè) Worker 進(jìn)程成功直接和客戶端建立連接,之后這個(gè)連接中所有的數(shù)據(jù)收發(fā)直接和這個(gè) Worker 通訊,不再經(jīng)過主進(jìn)程的 Reactor 線程轉(zhuǎn)發(fā)。

SWOOLE_PROCESS模式

SWOOLE_PROCESS 的所有客戶端都是和主進(jìn)程建立的,內(nèi)部實(shí)現(xiàn)比較復(fù)雜,用了大量的進(jìn)程間通信、進(jìn)程管理機(jī)制。適合業(yè)務(wù)邏輯非常復(fù)雜的場(chǎng)景,因?yàn)樗梢苑奖愕剡M(jìn)行進(jìn)程間的互相通信。

在 SWOOLE_PROCESS 中,所有的 Worker 不會(huì)去爭(zhēng)搶連接,也不會(huì)讓某一個(gè)連接與某個(gè)固定的 Worker 通訊,而是通過一個(gè)主進(jìn)程進(jìn)行連接。剩下的事件處理則交給不同的 Worker 去執(zhí)行,當(dāng)?shù)竭_(dá) Worker 之后,同樣地也是使用回調(diào)方式進(jìn)行處理了,后續(xù)內(nèi)容基本就和 BASE 差不多了。也就是說,Worker 功能的不同是它和 SWOOLE_BASE 最大的差異,它實(shí)現(xiàn)了連接與數(shù)據(jù)請(qǐng)求的分離,不會(huì)因?yàn)槟承┻B接數(shù)據(jù)量大某些量小導(dǎo)致 Worker 進(jìn)程不均衡。具體的方式就是在這種模式下,會(huì)多出來一個(gè)主管線程的進(jìn)程,其中還會(huì)有一個(gè)非常重要的 Reactor 線程,下面我們?cè)僭敿?xì)說明。

兩種模式的異同與優(yōu)缺點(diǎn)

如果客戶端之間不需要交互,也就是我們普通的 HTTP 服務(wù)的話,SWOOLE_BASE 模式是一個(gè)很好的選擇,但是它除了 send() 和 close() 方法之外,不支持跨進(jìn)程執(zhí)行。但其實(shí),這兩種模式在底層的處理上沒什么太大的區(qū)別,都是走的異步IO機(jī)制。只是說它們的連接方式不同。SWOOLE_BASE 的每個(gè) Worker 都可以看成是 SWOOLE_PROCESS 的 Reactor 線程和 Worker 進(jìn)程兩部分的組合。

我們可以來測(cè)試一下。

$http = new Swoole\Http\Server('0.0.0.0'9501, SWOOLE_BASE);

//$http = new Swoole\Http\Server('0.0.0.0', 9501, SWOOLE_PROCESS);

$http->set([
    'worker_num'=>2
]);

$http->on('Request'function ($request, $response) {
    var_dump(func_get_args());
    $response->end('開始測(cè)試');
});

$http->start();

通過切換上面兩個(gè)注釋,我們就可以查看兩種服務(wù)運(yùn)行模式的情況,可以通過 pstree -p 命令。

在 SWOOLE_BASE 模式下,輸出的內(nèi)容是這樣的。

可以看到,在 1629 這個(gè)進(jìn)程下面有兩個(gè)子進(jìn)程 1630 和 1631 。然后切換成 SWOOLE_PROCESS 模式,再查看進(jìn)程情況。


很明顯,這里不一樣了,在 1577 這個(gè) Master 主進(jìn)程下,有兩個(gè)進(jìn)程,一個(gè)是 1578 ,一個(gè)是 1579 它表示的是線程組,然后在 1578 Manager 管理進(jìn)程下面,又有 1580 和 1581 兩個(gè) Worker 進(jìn)程。

同樣地,我們使用之前在 【Swoole教程2.5】異步任務(wù)https://mp.weixin.qq.com/s/bQt9Ul-H34eUYw2-Qu-N0g 中的代碼來測(cè)試,可以看到 Task 異步任務(wù)也是起的進(jìn)程。(注意,我們?cè)跍y(cè)試代碼中設(shè)置的是 task_worker_num,沒有設(shè)置 worker_num ,所以是 1個(gè)Worker + 4個(gè) TaskWorker 進(jìn)程,最后再加一個(gè) PROCESS 模式的 線程組 )如果如圖所示。


到這里,相信你也看出了,SWOOLE_BASE 比 SWOOLE_PROCESS 少了一層進(jìn)程的遞進(jìn),也就是少了一個(gè)層級(jí)。在 SWOOLE_BASE 模式下,沒有 Master 進(jìn)程,只有一個(gè) Manager 進(jìn)程,另外就是沒有從 Master 中分出來的線程組。關(guān)于 Master/Manager/Reactor/TaskWorker 這些東西我們下一小節(jié)就會(huì)說到。

BASE 模式因?yàn)楦?jiǎn)單,所以不容易出錯(cuò),它也沒有 IPC 開銷,而 PROCESS 模式有 2 次 IPC 開銷,master 進(jìn)程與 worker 進(jìn)程需要 Unix Socket 進(jìn)行通信。IPC 這東西就是同一臺(tái)主機(jī)上兩個(gè)進(jìn)程間通信的簡(jiǎn)稱。它一般有兩種形式,一個(gè)是通過 Unix Socket 的方式,就是我們最常見的類似于 php-fcgi.sock 或者 mysql.sock 那種東西。另一種就是 sysvmsg 形式,這是 Linux 提供的一種消息隊(duì)列,一般使用的比較少。

當(dāng)然,BASE 模式也有它自身存在的問題,主要也是因?yàn)樯厦嬷v過的特性。由于 Worker 是和連接綁定的,因此,某個(gè) Worker 掛掉的話,這個(gè) Worker 里面的所有連接都會(huì)被關(guān)閉。另外由于爭(zhēng)搶的情況,Worker 進(jìn)程無法實(shí)現(xiàn)均衡,有可能有些連接數(shù)據(jù)量小,負(fù)載也會(huì)非常低。最后,如果回調(diào)函數(shù)中阻塞操作,會(huì)導(dǎo)致 Server 退化為同步模式,容易導(dǎo)致 TCP 的 backlog 隊(duì)列塞滿。不過就像上面說過的,Http 這種無狀態(tài)的不需要交互的連接,使用 BASE 沒什么問題,而且效率也是非常 OK 的。當(dāng)然,既然默認(rèn)情況下 Swoole 已經(jīng)為我們提供的是 SWOOLE_PROCESS 進(jìn)程了,那么也就說明 SWOOLE_PROCESS 模式是更加推薦的一種模式。

各種進(jìn)程問題

接下來,我們繼續(xù)學(xué)習(xí)上面經(jīng)常會(huì)提到的各種進(jìn)程和線程問題。

Master 進(jìn)程

它是一個(gè)多線程進(jìn)程。用于管理線程,它會(huì)創(chuàng)建 Master 線程以及 Reactor 線程,并且還有心跳檢測(cè)線程、UDP 收包線程等等。

Reactor 線程

這個(gè)線程我們不止一次提到了,它是在 Master 進(jìn)程中創(chuàng)建的,負(fù)責(zé)客戶端 TCP 連接、處理網(wǎng)絡(luò) IO 、處理協(xié)議、收發(fā)數(shù)據(jù),它不執(zhí)行任何 PHP 代碼,用于將 TCP 客戶端發(fā)來的數(shù)據(jù)緩沖、拼接、拆分成完整的一個(gè)請(qǐng)求數(shù)據(jù)包。我們?cè)?Swoole 代碼中操作不了線程,為什么呢?其實(shí) PHP 本身就是不支持多線程的,Swoole 是一種多進(jìn)程應(yīng)用框架。在這里的線程是在底層用 C/C++ 封裝好的。因此,也并沒有為我們提供可以直接操作線程的接口。但我們已經(jīng)學(xué)習(xí)過了,協(xié)程本身就是工作在線程之上的,而且,協(xié)程也已經(jīng)是現(xiàn)在的主流方向了,所以在 Swoole 中,進(jìn)程管理和協(xié)程,才是我們學(xué)習(xí)的重點(diǎn)。

Worker 進(jìn)程

Worker 是接受 Reactor 線程投遞過來的請(qǐng)求數(shù)據(jù)包,并執(zhí)行具體的 PHP 回調(diào)函數(shù)來進(jìn)行數(shù)據(jù)處理的。在處理完成之后,將生成的響應(yīng)數(shù)據(jù)發(fā)送回 Reactor 線程,再由 Reactor 發(fā)送給客戶端。Worker 進(jìn)程可以是異步非阻塞模式的,也可以是同步阻塞模式的,并且是以多進(jìn)程方式運(yùn)行的。

TaskWorker 進(jìn)程

它是接受收 Worker 進(jìn)程投遞過來的任務(wù),處理任務(wù)后將結(jié)果返回給 Worker 進(jìn)程,這種模式是同步阻塞的模式,同樣它也是以多進(jìn)程的方式運(yùn)行的。

Manager 進(jìn)程

這個(gè)進(jìn)程主要負(fù)責(zé)創(chuàng)建、回收 Worker/TaskWorkder 進(jìn)程。其實(shí)就是一個(gè)進(jìn)程管理器。

它們的關(guān)系

首先,我們先來看兩張圖,也是官網(wǎng)給出的圖,并根據(jù)這兩張圖再來看看官網(wǎng)給出的例子。


第一張圖主要是 Manager 和 Master 的功能。我們主要看第二張圖。


在這張圖中,我們可以看到,Manager 進(jìn)程創(chuàng)建、回收、管理最下面的 Worker 進(jìn)程和 Task 進(jìn)程。并且是通過操作系統(tǒng)的 fork() 函數(shù)創(chuàng)建的,這個(gè)東西如果學(xué)過操作系統(tǒng)的同學(xué)應(yīng)該不會(huì)陌生,fork() 就是創(chuàng)建子進(jìn)程的函數(shù)。子進(jìn)程間通過 Unix Socket 或者 MQ 隊(duì)列進(jìn)行通信。如果你是 BASE 模式,那么就不會(huì)有 Master 進(jìn)程,這個(gè)時(shí)候,每一個(gè) Worker 進(jìn)程自己會(huì)承擔(dān)起 Reactor 的功能,接收、響應(yīng)請(qǐng)求數(shù)據(jù)。

如果你是使用的 PROCESS 模式,那么上面的 Master 進(jìn)程就會(huì)創(chuàng)建各種線程,還記得那個(gè)大括號(hào)的線程組吧,這個(gè)可是 BASE 模式?jīng)]有的。它用于處理請(qǐng)求響應(yīng)問題,不用想,多線程方式對(duì)于連接請(qǐng)求來說效率會(huì)更高。這也是前面說過的兩種模式的優(yōu)缺點(diǎn)的具體體現(xiàn)。然后 Reactor 線程通過 Unix Socket 與 Worker 進(jìn)行通訊,完成數(shù)據(jù)向 Worker 的轉(zhuǎn)發(fā)與接收。

我們用官網(wǎng)給出的例子來再說明一下它們之間的關(guān)系。Reactor 就是 nginx,Worker 就是 PHP-FPM 。Reactor 線程異步并行地處理網(wǎng)絡(luò)請(qǐng)求,然后再轉(zhuǎn)發(fā)給 Worker 進(jìn)程中去處理。Reactor 和 Worker 間通過 Unix Socket 進(jìn)行通信。

在 PHP-FPM 的應(yīng)用中,經(jīng)常會(huì)將一個(gè)任務(wù)異步投遞到 Redis 等隊(duì)列中,并在后臺(tái)啟動(dòng)一些 PHP 進(jìn)程異步地處理這些任務(wù)。這個(gè)場(chǎng)景相信大家都不會(huì)陌生吧,比如說我們下了訂單之后,在原生 PHP 環(huán)境下進(jìn)行消息通知、郵件發(fā)送,我們都會(huì)直接將這種問題放到一個(gè)隊(duì)列中,然后讓后臺(tái)跑起一個(gè)腳本去消費(fèi)這些隊(duì)列從而進(jìn)行信息發(fā)送。而 Swoole 提供的 TaskWorker 則是一套更完整的方案,將任務(wù)的投遞、隊(duì)列、PHP 任務(wù)處理進(jìn)程管理合為一體。通過底層提供的 API 可以非常簡(jiǎn)單地實(shí)現(xiàn)異步任務(wù)的處理。另外 TaskWorker 還可以在任務(wù)執(zhí)行完成后,再返回一個(gè)結(jié)果反饋到 Worker。

一個(gè)更通俗的比喻,假設(shè) Server 就是一個(gè)工廠,那 Reactor 就是銷售,接受客戶訂單。而 Worker 就是工人,當(dāng)銷售接到訂單后,Worker 去工作生產(chǎn)出客戶要的東西。而 TaskWorker 可以理解為行政人員,可以幫助 Worker 干些雜事,讓 Worker 專心工作。

上述內(nèi)容需要好好理解,特別是對(duì)于我們些長年接觸傳統(tǒng)的 PHP-FPM 模式開發(fā)的同學(xué)來說,要轉(zhuǎn)換思維很不容易。不過根據(jù)官方提供的例子,相信大家也能很快把這個(gè)彎轉(zhuǎn)過來。普通的請(qǐng)求就是把我們的 Nginx+PHP-FPM 給結(jié)合起來了,而 Task 則是可以處理一些類似于消息隊(duì)列的異步操作。

Swoole 服務(wù)運(yùn)行流程

最后,我們?cè)賮砹私庖幌抡w Swoole 服務(wù)的運(yùn)行流程,同樣也是來自官網(wǎng)的圖片。


其實(shí)這個(gè)流程圖和我們的代碼流程非常類似。定義一個(gè) Server 對(duì)象,使用 set() 方法設(shè)置參數(shù),然后使用 on() 方法開始監(jiān)聽各種回調(diào),最后 start() 方法啟動(dòng)服務(wù)。在服務(wù)啟動(dòng)之后,創(chuàng)建了 Manager 進(jìn)程,如果是 PROCESS 模式的話,則是先創(chuàng)建一個(gè) Master 進(jìn)程,然后在 Master 之下創(chuàng)建 Manager 。接著,Manager 根據(jù) tasker_num 數(shù)量創(chuàng)建并管理相對(duì)應(yīng)數(shù)量的 Worker 進(jìn)程。其中,可以在 Worker 中創(chuàng)建異步的 task 進(jìn)程。

Reactor 線程在最外面處理請(qǐng)求響應(yīng)問題,監(jiān)聽相對(duì)應(yīng)的事件,并與 Worker 進(jìn)行通信。如果是 BASE 模式,不存在 Reactor 線程,則是全部由 Worker 來解決,而且它與連接的關(guān)系是一對(duì)一的。

總結(jié)

又是讓人暈頭轉(zhuǎn)向的一篇文章吧。在今天的學(xué)習(xí)中,最主要的其實(shí)還是一種思維的轉(zhuǎn)變,那就是我們要通過多進(jìn)程的方式來提供服務(wù)應(yīng)用。而且這種模式其實(shí)并不陌生,Nginx+PHP-FPM 就是這種模式,只不過,PHP-FPM 本身就是一個(gè)進(jìn)程管理工具,但它的效率以及實(shí)現(xiàn)方式都與 Swoole 略有差別。包括在 PHP8 之后的 JIT ,它就是通過 OPCahce 來實(shí)現(xiàn)的,也是在將大部分代碼全部一次加載到內(nèi)存中,就像 Swoole 一樣節(jié)約每次 PHP-FPM 的全量加載問題從而提升性能。

好好消化吸收一下吧,不過同樣的,如果上述內(nèi)容有錯(cuò)誤紕漏,隨時(shí)歡迎大家指正批評(píng),畢竟水平有限。

測(cè)試代碼:

https://github.com/zhangyue0503/swoole/blob/main/3.Swoole%E8%BF%9B%E7%A8%8B/source/3.2Swoole%E5%BC%82%E6%AD%A5%E8%BF%9B%E7%A8%8B%E7%B3%BB%E7%BB%9F.php

參考文檔:

https://wiki./#/server/init

https://wiki./#/learn?id=process-diff

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多