Swoole進(jìn)程模型

進(jìn)程

什么是進(jìn)程

進(jìn)程Process是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)分配資源和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。在早期面向進(jìn)程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是程序的基本執(zhí)行實(shí)體。在當(dāng)代面向線程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是線程的容器。簡(jiǎn)單來說,程序是指令、數(shù)據(jù)以及其組織形式的描述,而進(jìn)程則是程序的實(shí)體。

在操作系統(tǒng)中,進(jìn)程表示正在運(yùn)行的程序,例如在終端中使用PHP命令運(yùn)行PHP腳本,此時(shí)就相當(dāng)于創(chuàng)建了一個(gè)進(jìn)程,這個(gè)進(jìn)程會(huì)在系統(tǒng)中駐存,申請(qǐng)屬于它自己的內(nèi)存空間和系統(tǒng)資源,并且運(yùn)行相應(yīng)的程序。

$ php build.php
<?php
//獲取當(dāng)前進(jìn)程的PID
echo posix_getpid();
//修改所在進(jìn)程的名稱
swoole_set_process_name("swoole process master");
//模擬持續(xù)運(yùn)行100秒的程序
sleep(100);//持續(xù)運(yùn)行100秒的目的是為了在進(jìn)程中可以查看而不至于很快結(jié)束

運(yùn)行程序

$ php build.php
71

查看進(jìn)程

$ ps aux | grep 71
root         1  0.0  0.1  18188  1712 pts/0    Ss+  11:07   0:00 /bin/bash
root        71  0.0  3.0 340468 30788 pts/2    S+   13:41   0:00 swoole process master
root        76  0.0  0.0  11112   940 pts/1    S+   13:42   0:00 grep 71

對(duì)于一個(gè)進(jìn)程來說,最核心的內(nèi)容可分為兩部分:一部分是它的內(nèi)存,這個(gè)內(nèi)存是在創(chuàng)建初始時(shí)從系統(tǒng)中分配的,進(jìn)程中所有創(chuàng)建的變量都會(huì)存儲(chǔ)在內(nèi)存環(huán)境中。另一部分是上下文環(huán)境, 進(jìn)程是運(yùn)行在操作系統(tǒng)中的,對(duì)于程序而言,它的運(yùn)行依賴于操作系統(tǒng)分配的資源、操作系統(tǒng)的狀態(tài)以及程序自身的狀態(tài),這些就構(gòu)成了進(jìn)程的上下文環(huán)境。

父子進(jìn)程

父子進(jìn)程
  • 子進(jìn)程會(huì)復(fù)制父進(jìn)程的內(nèi)存空間和上下文環(huán)境
  • 子進(jìn)程會(huì)復(fù)制父進(jìn)程的IO句柄即fd描述符
  • 子進(jìn)程的內(nèi)存空間與父進(jìn)程的內(nèi)存空間是獨(dú)立,是互不影響的。
  • 修改子進(jìn)程的內(nèi)存空間并不會(huì)修改父進(jìn)程或其他子進(jìn)程的內(nèi)存空間

例如:父進(jìn)程通過fopen打開文件后得到一個(gè)IO句柄fd,子進(jìn)程復(fù)制父進(jìn)程后同樣會(huì)得到這個(gè)fd。如果父進(jìn)程和子進(jìn)程同時(shí)對(duì)一個(gè)文件進(jìn)行操作,會(huì)造成文件混亂,因此需要加互斥鎖。

例如:父進(jìn)程中的變量x=1,父進(jìn)程派生子進(jìn)程后,子進(jìn)程也會(huì)存在變量x=1,但是修改父進(jìn)程中的變量x并不會(huì)影響子進(jìn)程的變量x的值。

多進(jìn)程

PHP是單進(jìn)程執(zhí)行的,在處理高并發(fā)時(shí)主要依賴于Web服務(wù)器或PHP-FPM的多進(jìn)程管理以及進(jìn)程的復(fù)用,但在PHP實(shí)現(xiàn)多進(jìn)程尤其是后臺(tái)PHP-CLI模式下處理大量數(shù)據(jù)或運(yùn)行后臺(tái)Deamon守護(hù)進(jìn)程時(shí),多進(jìn)程的優(yōu)勢(shì)自然是最好的。

PHP的多線程也曾被人提及,但進(jìn)程內(nèi)多線程資源共享和分配問題難以解決,PHP有一個(gè)多線程過的擴(kuò)展pthreads,它要求PHP環(huán)境必須是線程安全的。

多進(jìn)程簡(jiǎn)單來說就是多個(gè)進(jìn)程同時(shí)執(zhí)行多個(gè)任務(wù),可以將耗時(shí)但又必須執(zhí)行的查詢分成多個(gè)子進(jìn)程進(jìn)行操作。

  • PHP多進(jìn)程不支持PHP-FPM和CGI模式,只能通過PHP-CLI模式。
  • PHP多進(jìn)程適用于定時(shí)任務(wù)執(zhí)行,互斥且耗時(shí)的任務(wù)。

開發(fā)使用PHP多進(jìn)程的場(chǎng)景也就是使用PHP-FPM,PHP-FPM作為PHP的多進(jìn)程管理器,當(dāng)使用Nginx作為WebServer時(shí),來自客戶端的請(qǐng)求會(huì)根據(jù)Nginx的路由配置,將以PHP為后綴的文件轉(zhuǎn)發(fā)給PHP-FPM。當(dāng)多個(gè)用戶同時(shí)請(qǐng)求API時(shí),PHP-FPM會(huì)開啟多個(gè)PHP的處理進(jìn)程進(jìn)行處理。

檢查PHP是否支持多進(jìn)程擴(kuò)展

$ php -m | grep pcntl

多進(jìn)程的優(yōu)勢(shì)

PHP相比C、C++、Java少了多線程,PHP中只有多進(jìn)程的方案,所以PHP中的全局變量和對(duì)象不是共享的,數(shù)據(jù)結(jié)構(gòu)也不能跨進(jìn)程操作,另外Socket文件描述符也不能共享...

多線程看似比多進(jìn)程強(qiáng)大的多,多線程的缺陷也同樣明顯:

  • 數(shù)據(jù)同步時(shí),要么犧牲性能到處加鎖,要么使用地獄難度的無鎖并發(fā)編程。
  • 當(dāng)程序邏輯復(fù)雜后,鎖會(huì)越來越難以控制。一旦死鎖,程序基本上就完了。
  • 某個(gè)線程掛掉后所有的線程都會(huì)退出

相比較多線程,多進(jìn)程擁有的優(yōu)勢(shì)是

  • 配合進(jìn)程間通信,基本可以實(shí)現(xiàn)任意數(shù)據(jù)共享。
  • 多進(jìn)程不需要鎖
  • 多進(jìn)程可以共享內(nèi)存的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)一些多線程的功能

對(duì)于并發(fā)服務(wù)器核心是IO,并非大規(guī)模密集運(yùn)算,高并發(fā)的服務(wù)器單機(jī)能維持10W連接,每秒可以處理3~5W筆消息收發(fā)。

普通的Web應(yīng)用都是IO密集型的程序,瓶頸在MySQL上,所以體現(xiàn)不出PHP的性能優(yōu)勢(shì)。但在密集計(jì)算方面比C/C++、Java等靜態(tài)編譯語(yǔ)言相差幾十倍甚至上百倍。

例如:使用多進(jìn)程方式同時(shí)訪問Web地址

$ vim multi.php
<?php
echo "process begin: ".date("Y-m-d H:i:s").PHP_EOL;

//初始化地址數(shù)組
$urls = [
    "http://www.baidu.com",
    "http://www.360.com",
    "http://www.qq.com",
    "http://www.sina.com"
];
//初始化數(shù)組用于回收線程管道內(nèi)容
$workers = [];
//按照任務(wù)分配線程
for($i=0; $i<count($urls); $i++){
    $url = $urls[$i];
    //創(chuàng)建進(jìn)程
    $process = new swoole_process(function(swoole_process $worker) use($url){
        //模擬執(zhí)行耗時(shí)任務(wù)
        file_get_contents($url);
        //sleep(1);//模擬耗時(shí)1秒
        echo $url.PHP_EOL;
    }, true);
    //開啟進(jìn)程
    $pid = $process->start();
    $workers[$pid] = $process;
}
//打印管道內(nèi)容
foreach($workers as $worker){
    echo "pid : ".$worker->read();
}

echo "process end: ".date("Y-m-d H:i:s").PHP_EOL;

運(yùn)行代碼

$ php multi.php
process begin: 2019-06-22 16:19:48
pid : http://www.baidu.com
pid : http://www.360.com
pid : http://www.qq.com
pid : http://www.sina.com
process end: 2019-06-22 16:19:49

內(nèi)存共享

進(jìn)程之間是相互獨(dú)立的,那么如何實(shí)現(xiàn)進(jìn)程之間的通信呢? 這里可以使用共享內(nèi)存的方式來實(shí)現(xiàn)。

共享內(nèi)存ShareMemory是映射一段能被其他進(jìn)程所訪問的內(nèi)存,這段共享內(nèi)存由一個(gè)進(jìn)程創(chuàng)建,但多個(gè)進(jìn)程都可以訪問。共享內(nèi)存是最快的IPC方式,是針對(duì)其他進(jìn)程之間通信效率低下而專門設(shè)計(jì)的,它往往與其它通信機(jī)制,如信號(hào)量配置使用以實(shí)現(xiàn)進(jìn)程之間的同步和通信。

共享內(nèi)存是操作系統(tǒng)中比較特殊的內(nèi)存,它并不依賴于任何進(jìn)程, 也不屬于任何進(jìn)程。通過調(diào)用系統(tǒng)函數(shù)創(chuàng)建共享內(nèi)存,并指定它的索引,也就是它的IDshmid,通過索引任何進(jìn)程都可以在共享內(nèi)存中申請(qǐng)內(nèi)存空間并存儲(chǔ)對(duì)應(yīng)的值。

共享內(nèi)存
  • 共享內(nèi)存并不屬于任何一個(gè)進(jìn)程
  • 在共享內(nèi)存中分配的內(nèi)存空間可以被任何進(jìn)程訪問
  • 即使進(jìn)程關(guān)閉,共享內(nèi)存仍然可以繼續(xù)保存在操作系統(tǒng)中。

查看操作系統(tǒng)中共享內(nèi)存的分片

$ ipcs -m
------------ 共享內(nèi)存段 --------------
鍵        shmid      擁有者  權(quán)限     字節(jié)     連接數(shù)  狀態(tài)      
0x00000000 131072     jc         777        16384      1          目標(biāo)       
0x00000000 327681     jc         600        67108864   2          目標(biāo)       
0x00000000 262146     jc         777        8077312    2          目標(biāo)     

Swoole沒有采用多線程模型而使用了多線程模型,在一定程度上減少了訪問數(shù)據(jù)時(shí)加鎖解鎖的開銷,但同時(shí)也引入了新的需求 共享內(nèi)存。Swoole中為了更好的進(jìn)行內(nèi)存管理,減少頻繁分配釋放內(nèi)存空間造成的損耗和內(nèi)存碎片,Rango實(shí)際并實(shí)現(xiàn)了三種不同功能的內(nèi)存池分別時(shí)FixedPoolRingBuffer、MemoryGlobal。

Swoole開發(fā)模式

對(duì)于傳統(tǒng)PHP的Web開發(fā)而言,最常用的是LNMP架構(gòu)。在LNMP架構(gòu)中,當(dāng)請(qǐng)求進(jìn)入時(shí),WebServer會(huì)將請(qǐng)求轉(zhuǎn)交給PHP-FPM,PHP-FPM是一個(gè)進(jìn)程池架構(gòu)的FastCGI服務(wù),內(nèi)置了PHP解釋器。PHP-FPM負(fù)責(zé)解釋執(zhí)行PHP文件并生成響應(yīng),最終返回給WebServer展現(xiàn)至前端。由于PHP-FPM本身是同步阻塞進(jìn)程模型,在請(qǐng)求結(jié)束后會(huì)釋放掉所有資源,包括框架初始化創(chuàng)建的一些列對(duì)象,從而導(dǎo)致PHP進(jìn)程進(jìn)入“空轉(zhuǎn)”消耗大量CPU資源,最終導(dǎo)致單機(jī)的吞吐能力有限。

另外,在每次請(qǐng)求處理的過程都意味著一次PHP文件解析、環(huán)境設(shè)置等不必要的耗時(shí)操作,當(dāng)PHP進(jìn)程處理完后就會(huì)銷毀,無法在PHP程序中使用連接池等技術(shù)實(shí)現(xiàn)性能優(yōu)化。

針對(duì)傳統(tǒng)架構(gòu)的問題,Swoole從PHP擴(kuò)展下手,解決了上述問題。相比較傳統(tǒng)的Web架構(gòu),Swoole進(jìn)程模型最大的特點(diǎn)在于多線程Reactor模式處理網(wǎng)絡(luò)請(qǐng)求,使其能輕松應(yīng)對(duì)大量連接。

除此之外,Swoole是全異步非阻塞,因此占用資源少,程序執(zhí)行效率高。在Swoole中程序運(yùn)行只解析加載一次PHP文件,避免每次請(qǐng)求的重復(fù)加載。再者,Swoole進(jìn)程常駐,使得連接池和請(qǐng)求之間的信息傳遞的實(shí)現(xiàn)成為可能。

使用Swoole開發(fā)時(shí),需要開發(fā)人員對(duì)多進(jìn)程的運(yùn)行模式有著清晰的認(rèn)識(shí)。另外,Swoole很容易造成內(nèi)存泄露。在處理全局變量、靜態(tài)變量的時(shí)候要小心,這種不會(huì)被GC清理的變量會(huì)存在整個(gè)生命周期中。如果沒有正確的處理,很容易消耗完內(nèi)存。而在PHP-FPM下,PHP代碼執(zhí)行完畢內(nèi)存就會(huì)被完全釋放掉。

Swoole進(jìn)程結(jié)構(gòu)

LNMP架構(gòu)中PHP是需要依賴Nginx這樣的Web服務(wù)器以及PHP-FPM這樣的多進(jìn)程的PHP解析器。當(dāng)一個(gè)請(qǐng)求到來時(shí)PHP-FPM會(huì)去創(chuàng)建一個(gè)新的進(jìn)程去處理這個(gè)請(qǐng)求,在這種情況下,系統(tǒng)的開銷很大程序上都用在創(chuàng)建和銷毀進(jìn)程上,導(dǎo)致了程序的響應(yīng)效率并不是非常高。

Swoole的強(qiáng)大之處在于進(jìn)程模型的設(shè)計(jì),即解決了異步問題,又解決了并發(fā)問題。

Swoole的進(jìn)程可分為四種角色

  • Master進(jìn)程
    保證Swoole機(jī)制運(yùn)行,同時(shí)利用它創(chuàng)建Master主線程(負(fù)責(zé)接收連接、定時(shí)器等)和Reactor線程(處理連接并將請(qǐng)求分發(fā)給各個(gè)Worker進(jìn)程)。
  • Manager進(jìn)程
    Worker進(jìn)程和Task進(jìn)程均由Manager進(jìn)程派生,Manager管理進(jìn)程負(fù)責(zé)結(jié)束時(shí)回收子進(jìn)程,避免僵尸進(jìn)程的存在。
  • Worker進(jìn)程
    用PHP回調(diào)函數(shù)處理由Reactor分發(fā)過來的請(qǐng)求數(shù)據(jù),并生成響應(yīng)數(shù)據(jù)發(fā)送給Reactor,由Reactor發(fā)送給TCP客戶端。
  • Task進(jìn)程
    接收由Worker進(jìn)程分發(fā)給它的任務(wù),以多進(jìn)程方式運(yùn)行,處理好后將結(jié)果返回給它的Worker進(jìn)程。
Swoole進(jìn)程

Swoole中采用了和PHP-FPM完全不同的架構(gòu),整個(gè)Swoole擴(kuò)展可以分為三層:

Swoole進(jìn)程模型

第1層:Master主進(jìn)程

Master主進(jìn)程

Master進(jìn)程是Swoole的主進(jìn)程,主要用于處理Swoole的核心事件驅(qū)動(dòng)。Master主進(jìn)程是一個(gè)多線程模型,擁有多個(gè)獨(dú)立的Reactor線程。

Master主進(jìn)程包含Master線程、Reactor線程、心跳檢測(cè)線程、UDP收包線程。每個(gè)Reactor子線程中都運(yùn)行著一個(gè)epoll函數(shù)的實(shí)例,Swoole對(duì)于事件的監(jiān)聽都會(huì)在Reactor線程中實(shí)現(xiàn),比如來自客戶端的連接、本地通信使用的管道、異步操作使用的文件以及文件描述符都會(huì)注冊(cè)在epoll函數(shù)中。

Master主進(jìn)程使用select/poll進(jìn)行IO事件循環(huán),Master主進(jìn)程中的文件描述符只有幾個(gè),Reactor線程使用epoll,因?yàn)镽eactor線程中會(huì)監(jiān)聽大量連接的可讀事件,使用epoll可以支持大量的文件描述符。

HTTP服務(wù)器為例,Master主進(jìn)程負(fù)責(zé)監(jiān)聽端口,然后接收新的連接,并將這個(gè)連接分配給一個(gè)Reactor線程,由這個(gè)Reactor線程監(jiān)聽此連接,一旦此連接可讀時(shí),它會(huì)讀取數(shù)據(jù)并解析協(xié)議,然后將請(qǐng)求投遞到Worker工作進(jìn)程中去執(zhí)行。

Master主進(jìn)程內(nèi)的回調(diào)函數(shù)

  • onStart 服務(wù)器啟動(dòng)時(shí)主進(jìn)程的主線程回調(diào)此函數(shù)
  • onShutdown 服務(wù)器正常結(jié)束時(shí)發(fā)生

Master 線程

Swoole啟動(dòng)后Master主線程會(huì)負(fù)責(zé)監(jiān)聽服務(wù)器的socket,如果有新的連接accept,Master主線程會(huì)評(píng)估每個(gè)Reactor線程的連接數(shù)量,并將此連接分配給連接最少的Reactor線程。這樣做的好處是:

  • 每個(gè)Reactor線程持有的連接數(shù)非常均衡,沒有單個(gè)線程負(fù)載過高的問題。
  • 解決了驚群?jiǎn)栴},尤其是擁有多個(gè)listen socket時(shí),節(jié)約了線程喚醒和切換的開銷。
  • 主線程接管了所有信號(hào)signal的處理,使Reactor線程運(yùn)行中可以不被信號(hào)打斷。

主線程Master在accept新的連接后,會(huì)將這個(gè)連接分配給一個(gè)固定的Reactor線程,并由這個(gè)線程負(fù)責(zé)監(jiān)聽此socket,在socket可讀時(shí)讀取數(shù)據(jù),并進(jìn)行協(xié)議解析,最后將請(qǐng)求投遞到Worker進(jìn)程。

Master主線程

Reactor線程

  • 負(fù)責(zé)維護(hù)客戶端TCP連接、處理網(wǎng)絡(luò)IO、處理協(xié)議、收發(fā)數(shù)據(jù)。
  • 完全是異步非阻塞的模式
  • 全部都是C代碼,除了Start/Shutdown事件回調(diào)外,不執(zhí)行任何PHP代碼。
  • TCP客戶端發(fā)送來的數(shù)據(jù)緩沖、拼接、拆分為完整的請(qǐng)求數(shù)據(jù)包。
  • Reactor以多線程的方式運(yùn)行

Swoole擁有多線程Reactor,所以可以充分利用多核,開啟CPU親和設(shè)置后,Reactor線程可以綁定單獨(dú)的核,節(jié)省CPU Cache開銷。

Reactor線程負(fù)責(zé)處理TCP連接,是收發(fā)數(shù)據(jù)的線程。Swoole的Master主線程在accept新的連接后,會(huì)將這個(gè)連接分配給一個(gè)固定的Reactor線程,并由這個(gè)線程負(fù)責(zé)監(jiān)聽此socket。在socket可讀時(shí)讀取數(shù)據(jù),并進(jìn)行協(xié)議解析,將請(qǐng)求投遞到Worker工作進(jìn)程。在socket可寫時(shí),將數(shù)據(jù)發(fā)送給TCP客戶端。

Reactor線程負(fù)責(zé)維護(hù)客戶端TCP連接、處理網(wǎng)絡(luò)IO、處理協(xié)議、收發(fā)數(shù)據(jù),它完全是異步非阻塞的模式。
Reactor線程是全異步非阻塞的,即使Worker進(jìn)程采用了同步模式,依然不響應(yīng)Reactor線程的性能。在Worker進(jìn)程組很繁忙的狀態(tài)下,Reactor線程完全不受影響,依然可以收發(fā)處理數(shù)據(jù)。

由于TCP是流式的沒有邊界,所以處理起來很麻煩。Reactor線程可以使用EOF或者包頭長(zhǎng)度,自動(dòng)緩存數(shù)據(jù)、組裝數(shù)據(jù)包,等一個(gè)請(qǐng)求完全收到后,再次遞交給Worker。

Reactor全部是C代碼,除了Start/Shutdown事件回調(diào)外,不執(zhí)行任何PHP代碼。它將TCP客戶端發(fā)來的數(shù)據(jù)緩沖、拼接、拆分成完整的一個(gè)請(qǐng)求數(shù)據(jù)包。

綜上所述,Master主進(jìn)程中包含兩個(gè)關(guān)鍵線程:Master主線程和Reactor線程,Master主線程用來處理accept()事件,創(chuàng)建新的socket fd,當(dāng)它接收到新連接后會(huì)將新的socket連接放到Reactor線程的事件監(jiān)聽循環(huán)中,Reactor線程負(fù)責(zé)接收從客戶端發(fā)送過來的數(shù)據(jù),并按協(xié)議解析后通過管道pipe傳遞給Worker工作進(jìn)程進(jìn)行處理。Worker工作進(jìn)程處理完畢后,會(huì)將結(jié)果通過管道pipe回傳給Reactor線程,Reactor線程再按照協(xié)議將結(jié)果通過socket發(fā)送給客戶端。可以看到Reactor線程負(fù)責(zé)數(shù)據(jù)的IO和傳輸,在Linux系統(tǒng)下這些IO事件都是通過epoll機(jī)制來處理的。

心跳包檢測(cè)線程HeartbeatCheck

Swoole配置了心跳檢測(cè)后心跳包線程會(huì)在固定事件內(nèi)對(duì)所有之前在線的連接發(fā)送檢測(cè)數(shù)據(jù)包。

UDP收包線程UdpRecv

接收并處理客戶端UDP數(shù)據(jù)包

第2層:Manager管理進(jìn)程

Swoole運(yùn)行中會(huì)創(chuàng)建一個(gè)單獨(dú)的管理進(jìn)程,所有的Worker進(jìn)程和Task進(jìn)程都是從管理進(jìn)程fork創(chuàng)建出來的。

Manager管理進(jìn)程會(huì)監(jiān)聽所有子進(jìn)程的退出事件,當(dāng)Worker進(jìn)程發(fā)生致命錯(cuò)誤或運(yùn)行生命周期結(jié)束時(shí),Manager管理進(jìn)程會(huì)回收此進(jìn)程并創(chuàng)建新的進(jìn)程。

Manager管理進(jìn)程還可以平滑地重啟所有工作進(jìn)程Worker,以實(shí)現(xiàn)程序代碼的重新加載。

Manager管理進(jìn)程管理著Worker工作進(jìn)程或Task任務(wù)進(jìn)程,Worker工作進(jìn)程或Task任務(wù)進(jìn)程都被Manager管理進(jìn)程fork創(chuàng)建并管理著。

Manager進(jìn)程負(fù)責(zé)創(chuàng)建和管理下層的Worker進(jìn)程組和Task進(jìn)程組,Manager進(jìn)程中不會(huì)運(yùn)行任何用戶層面的業(yè)務(wù)邏輯,僅僅只做進(jìn)程的管理和分配。

Manager進(jìn)程會(huì)fork創(chuàng)建出指定數(shù)量的Worker進(jìn)程和Task進(jìn)程。

Manager進(jìn)程的工作職責(zé)

  • Worker工作進(jìn)程和Task任務(wù)進(jìn)程都是由Manager管理進(jìn)程fork創(chuàng)建并管理的
  • 子進(jìn)程結(jié)束運(yùn)行時(shí),Manager管理進(jìn)程負(fù)責(zé)回收子進(jìn)程,以避免成為僵尸進(jìn)程,并創(chuàng)建新的子進(jìn)程。
  • 服務(wù)器關(guān)閉時(shí),Manager管理進(jìn)程發(fā)送信號(hào)給所有子進(jìn)程,并通知子進(jìn)程關(guān)閉服務(wù)。
  • 服務(wù)器重啟時(shí),Manager管理進(jìn)程會(huì)逐個(gè)關(guān)閉或重啟子進(jìn)程。

Manager進(jìn)程內(nèi)的回調(diào)函數(shù)

  • onManagerStart 當(dāng)管理進(jìn)程啟動(dòng)時(shí)調(diào)用
  • onManagerStop 當(dāng)管理進(jìn)程結(jié)束時(shí)調(diào)用
  • onWorkerError 當(dāng)Worker進(jìn)程或Task進(jìn)程發(fā)生異常后會(huì)在Manager進(jìn)程會(huì)回調(diào)此函數(shù)

第3層:工作進(jìn)程

  • 工作進(jìn)程主要用于處理客戶端請(qǐng)求
  • 工作進(jìn)程接收由Reactor線程投遞的請(qǐng)求數(shù)據(jù)包,并執(zhí)行PHP回調(diào)函數(shù)處理數(shù)據(jù)。
  • 工作進(jìn)程生成響應(yīng)數(shù)據(jù)并發(fā)送給Reactor線程,由Reactor線程發(fā)送給TCP客戶端。
  • 工作進(jìn)程可以是異步非阻塞模式也可以是同步阻塞模式。
  • 工作進(jìn)程以多進(jìn)程的方式運(yùn)行

與傳統(tǒng)的半同步半異步服務(wù)器不同是,Swoole的工作進(jìn)程可以同步的也可以異步的。這樣帶來了工作進(jìn)程類似于PHP-FPM進(jìn)程,它接收由Reactor線程投遞的請(qǐng)求數(shù)據(jù)包,并執(zhí)行PHP回調(diào)函數(shù)處理數(shù)據(jù)。工作線程生成響應(yīng)數(shù)據(jù)并發(fā)送給Reactor線程,由Reactor線程發(fā)送給TCP客戶端。工作線程可以是異步模式,也可以是同步模式。另外,工作線程以多進(jìn)程的方式運(yùn)行。

Swoole想要實(shí)現(xiàn)最好的性能就必須創(chuàng)建出多個(gè)工作進(jìn)程幫助處理任務(wù),但是工作進(jìn)程必須fork操作,而fork操作又是不安全的。如果沒有管理將會(huì)出現(xiàn)很多僵尸進(jìn)程,進(jìn)而影響服務(wù)器性能。同時(shí)工作進(jìn)程被誤殺或由于程序原因會(huì)引起異常退出,為了保證服務(wù)的穩(wěn)定性,需要重新創(chuàng)建工作進(jìn)程。

工作進(jìn)程可分為兩類:Worker進(jìn)程和Task進(jìn)程

  • Worker進(jìn)程是Swoole的主邏輯進(jìn)程,用于處理來自客戶端的請(qǐng)求。

  • Task進(jìn)程是Swoole提供的異步工作進(jìn)程,用于處理耗時(shí)較長(zhǎng)的同步任務(wù)。

Worker工作進(jìn)程

Worker工作進(jìn)程接收Reactor線程投遞過來的數(shù)據(jù),執(zhí)行PHP代碼,然后生成數(shù)據(jù)并交給Reactor線程,由Reactor線程通過TCP將數(shù)據(jù)返回給客戶端。如果是UDP,Worker工作進(jìn)程會(huì)直接將數(shù)據(jù)發(fā)送給客戶端。

Worker進(jìn)程中執(zhí)行的PHP代碼,它等同于PHP-FPMPHP-FPM在處理異步操作時(shí)是很無力的,但Swoole提供的Task進(jìn)程可以很好的解決這個(gè)問題。Worker進(jìn)程可以將一些異步任務(wù)投遞給Task進(jìn)程,然后直接返回,處理其他由Reactor線程投遞過來的事件。

Worker進(jìn)程內(nèi)的回調(diào)函數(shù)

  • onWorkerStart 當(dāng)Worker工作進(jìn)程或Task任務(wù)進(jìn)程啟動(dòng)時(shí)觸發(fā)
  • onWorkerStop 當(dāng)Worker進(jìn)程終止時(shí)觸發(fā)
  • onConnect 當(dāng)有新的連接進(jìn)入時(shí)觸發(fā)
  • onClose 當(dāng)TCP客戶端連接關(guān)閉后觸發(fā)
  • onReceive 當(dāng)接收到數(shù)據(jù)時(shí)觸發(fā)
  • onPacket 當(dāng)接收到UDP數(shù)據(jù)包是時(shí)觸發(fā)
  • onFinish 當(dāng)Worker工作進(jìn)程投遞的任務(wù)在task_worker中完成時(shí),Task進(jìn)程會(huì)通過finish()方法將任務(wù)處理的結(jié)果發(fā)送給Worker進(jìn)程。
  • onWorkerExit 當(dāng)開啟reload_async特性后有效,即異步重啟特性。
  • onPipeMessage 當(dāng)工作進(jìn)程收到由sendMessage發(fā)送的管道消息時(shí)觸發(fā)

Task任務(wù)進(jìn)程

  • 異步工作進(jìn)程
  • 接收由Worker工作進(jìn)程通過swoole_server->taskswoole_server->taskwait方法投遞的任務(wù)。
  • 處理任務(wù),并將結(jié)果數(shù)據(jù)返回給Worker工作進(jìn)程swoole_server->finish。
  • 同步阻塞模式
  • 以多進(jìn)程的方式運(yùn)行

Swoolen除了Reactor線程,Task任務(wù)工作進(jìn)程是以異步的方式處理其它任務(wù)的進(jìn)程,使用方式類似于Gearman。它接收由Worker進(jìn)程通過swoole_server->task/taskwait方法投遞的任務(wù),然后處理任務(wù),并將結(jié)果數(shù)據(jù)使用swoole_server->finish返回給Worker進(jìn)程。Task以多進(jìn)程的方式進(jìn)行運(yùn)行。

簡(jiǎn)單來說,可以將Reactor理解為Nginx,將Worker理解為PHP-FPM。Reactor線程異步并行地處理網(wǎng)絡(luò)請(qǐng)求,然后再轉(zhuǎn)發(fā)給Worker工作進(jìn)程中去處理(在回調(diào)函數(shù)中處理)。Reactor和Worker之間通過UnixSocket進(jìn)行通信。

Swoole除了Reactor線程,Worker工作進(jìn)程還提供了Task進(jìn)程池。目的是為了解決業(yè)務(wù)代碼中,有些邏輯部分不需要馬上執(zhí)行。利用Task進(jìn)程池,可以方便的投遞一個(gè)異步任務(wù)區(qū)執(zhí)行。

Task進(jìn)程以完全同步阻塞的方式運(yùn)行,一個(gè)Task進(jìn)程在執(zhí)行任務(wù)期間是不接受從Worker進(jìn)程投遞的任務(wù)的,當(dāng)Task進(jìn)程執(zhí)行完任務(wù)后,會(huì)異步的通知Worker進(jìn)程并告訴它任務(wù)已經(jīng)完成。

Task進(jìn)程內(nèi)的回調(diào)函數(shù)

  • onTask 在Task線程內(nèi)被調(diào)用,Worker進(jìn)程可使用swoole_server_task函數(shù)向Task進(jìn)程投遞新的任務(wù)。
  • onWorkerStart 在Worker或Task進(jìn)程啟動(dòng)時(shí)觸發(fā)
  • onPipeMessage 當(dāng)工作進(jìn)程收到由sendMessage發(fā)送的管道消息時(shí)觸發(fā)
Swoole的進(jìn)程

Swoole進(jìn)程協(xié)作

  1. 當(dāng)客戶端主動(dòng)連入服務(wù)器的時(shí)候,客戶端實(shí)際上是與Master主進(jìn)程中的某個(gè)Reactor線程發(fā)生了連接。
  2. 當(dāng)TCP三次握手成功后,由這個(gè)Reactor線程將連接成功的消息告知Manager管理進(jìn)程,再由Manager管理進(jìn)程轉(zhuǎn)交給Worker工作進(jìn)程,最終在Worker工作進(jìn)程中觸發(fā)onConnect事件對(duì)應(yīng)的方法。
  3. 當(dāng)客戶端向服務(wù)器發(fā)送一個(gè)數(shù)據(jù)包的時(shí)候,首先接收到數(shù)據(jù)包的是Reactor線程,同時(shí)Reactor線程會(huì)完成組包,再將組裝好的包交給Manager管理進(jìn)程,由Manager管理進(jìn)程轉(zhuǎn)交給Worker工作進(jìn)程,此時(shí)Worker工作進(jìn)程觸發(fā)onReceive事件。
  4. 如果Worker工作進(jìn)程中做了處理操作后再使用Send方法將數(shù)據(jù)發(fā)回給客戶端時(shí),數(shù)據(jù)會(huì)沿著這個(gè)路徑逆流而上。
  5. Task任務(wù)進(jìn)程用來處理一些占用時(shí)間較長(zhǎng)的業(yè)務(wù),主要處理Worker工作進(jìn)程中占用時(shí)間較長(zhǎng)的任務(wù)。

形象來說

  • Master主進(jìn)程 = 業(yè)務(wù)窗口
  • Reactor線程 = 前臺(tái)接待員
  • Manager管理進(jìn)程 = 項(xiàng)目經(jīng)理
  • Worker工作進(jìn)程 = 工人

當(dāng)在業(yè)務(wù)窗口辦理業(yè)務(wù)時(shí),如果用戶很多,后邊的用戶需要排隊(duì)等待服務(wù),Reactor負(fù)責(zé)與客戶直接溝通,對(duì)客戶的請(qǐng)求進(jìn)行初步的整理(傳輸層級(jí)別的整理,組包),然后Manager負(fù)責(zé)將業(yè)務(wù)分配給合適的Worker,如空閑的Worker,最終Worker負(fù)責(zé)實(shí)現(xiàn)具體的業(yè)務(wù)。

Swoole進(jìn)程關(guān)系

Reactor和Worker與Task的關(guān)系,簡(jiǎn)單理解可認(rèn)為:Reactor = Nginx、Worker = PHP-FPM

Reactor線程異步并行地處理網(wǎng)絡(luò)請(qǐng)求,然后再轉(zhuǎn)發(fā)給Worker工作進(jìn)程中去處理。

Reactor線程和Worker工作進(jìn)程之間通過socket進(jìn)行通信。

在PHP-FPM的應(yīng)用中,經(jīng)常會(huì)將一個(gè)任務(wù)異步投遞到Redis等隊(duì)列中,并在后臺(tái)啟動(dòng)一些PHP進(jìn)程異步地處理這些任務(wù)。

Swoole提供的Worker工作進(jìn)程是一套更加完整的方案,它將任務(wù)投遞、隊(duì)列、PHP任務(wù)進(jìn)程管理融為一體。通過底層的API實(shí)現(xiàn)異步任務(wù)的處理。另外,Task任務(wù)進(jìn)程可以在任務(wù)執(zhí)行完畢后,再返回一個(gè)結(jié)果反饋到Worker工作進(jìn)程。

Swoole的Reactor、Worker、Task之間可以緊密的結(jié)合起來,提供更加高級(jí)的使用方式。

假設(shè)Server是一個(gè)工廠

  • Reactor:銷售,接收客戶訂單。
  • Worker:工人,當(dāng)銷售接單后,Worker去工作生產(chǎn)出客戶需要的東西。
  • Task:行政人員,幫助Worker干些雜事兒,讓W(xué)orker專心工作。

底層會(huì)為Worker工作進(jìn)程、Task任務(wù)進(jìn)程分配一個(gè)唯一的ID,不同的Worker和Task任務(wù)進(jìn)程之間可以通過sendMessage接口進(jìn)行通信。

Swoole執(zhí)行流程

Swoole執(zhí)行流程
  1. 當(dāng)客戶端請(qǐng)求進(jìn)入Master主進(jìn)程后會(huì)被Master主線程接收到
  2. 將讀寫操作的監(jiān)聽注冊(cè)到對(duì)應(yīng)的Reactor線程中,并通知Worker工作進(jìn)程處理onConnect,也就是接收到連接的回調(diào)。
  3. 客戶端的數(shù)據(jù)會(huì)通知對(duì)應(yīng)的Reactor線程并發(fā)送給Worker工作進(jìn)程進(jìn)行處理。
  4. 如果Worker工作進(jìn)程投遞任務(wù),將數(shù)據(jù)通過管道發(fā)送給Task任務(wù)進(jìn)程,Task任務(wù)進(jìn)程處理完后會(huì)發(fā)送給Worker工作進(jìn)程。
  5. Worker工作進(jìn)程會(huì)通知Reactor線程發(fā)送數(shù)據(jù)給客戶端。
  6. 當(dāng)Worker工作進(jìn)程出現(xiàn)異常時(shí)關(guān)閉,Manager管理進(jìn)程會(huì)重新創(chuàng)建一個(gè)Worker工作進(jìn)程,保證Worker工作進(jìn)程的數(shù)量是固定的。
Swoole執(zhí)行流程
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前文再續(xù),就書接上一回,隨著與Server、TCP、Protocol的邂逅,Swoole終于迎來了自己的故事,今天...
    蝸牛淋雨閱讀 1,900評(píng)論 1 14
  • swoole的強(qiáng)大之處就在與其進(jìn)程模型的設(shè)計(jì),既解決了異步問題,又解決了并行。 主線程MainReactor sw...
    Yagami_閱讀 665評(píng)論 0 0
  • 前言 前文再續(xù),就書接上一回,隨著與Server、TCP、Protocol的邂逅,Swoole終于迎來了自己的故事...
    零一間閱讀 5,376評(píng)論 0 17
  • 進(jìn)程基本概念 狹義定義:進(jìn)程是正在運(yùn)行的程序的實(shí)例(an instance of a computer progr...
    zshanjun閱讀 858評(píng)論 1 0
  • 天外飄雪,室內(nèi)如春,康寶甜睡,我在畫荷,盼夏快至,帶兒外耍。
    榛木月月閱讀 172評(píng)論 3 1

友情鏈接更多精彩內(nèi)容