初識server一文的時候我們說過,swoole是事件驅動的。在使用swoole的過程中,我們也體會到,swoole的使用非常簡單,僅僅註冊相應的回調處理我們的業務邏輯即可。
但是,在繼續學習swoole之前,我們有必要再看一看swoole的運行流程和進程模型。
推薦(免費):swoole
#前面兩篇文章我們已經對server和task做了簡單的介紹,後面再對server的創作以及腳本的執行,如無特殊說明均在CLI下執行,我就不囉嗦了。
現在,我們建立一個簡單的server來分析一下,檔案命名為server-process.php
$serv = new swoole_server('127.0.0.1', 9501); $serv->set([ 'worker_num' => 2, 'task_worker_num' => 1, ]); $serv->on('Connect', function ($serv, $fd) { }); $serv->on('Receive', function ($serv, $fd, $fromId, $data) { }); $serv->on('Close', function ($serv, $fd) { }); $serv->on('Task', function ($serv, $taskId, $fromId, $data) { }); $serv->on('Finish', function ($serv, $taskId, $data) { }); $serv->start();
注意這裡我們選擇了兩個worker進程個一個task進程,那是不是就代表創建這個server就是開啟了3個進程呢?我們來看下
新開一個終端,我們用ps指令看下結果
$ ps aux | grep server-process root 21843 xxx... php server-process.php root 21844 xxx... php server-process.php root 21846 xxx... php server-process.php root 21847 xxx... php server-process.php root 21848 xxx... php server-process.php root 21854 xxx... grep --color=auto server-process
為了方便閱讀,ps的結果中部分不重要資料已經被稍加處理了。
排除最後一個結果(最後一個是我們運行的ps命令)我們發現,竟然有多達5個相似的進程在運行,按照我們理解,不應該是3個嗎,怎麼多了兩個呢?
還記得我們在進程/執行緒一文中說過的多進程的實作嗎?我們說到多進程的實作一般會被設計Master-Worker模式,常見的nginx預設的多進程模式也正是如此,當然swoole預設的也是多進程模型。
比較Master-Worker模式,swoole的行程模型可以用Master-Manager-Worker來形容。即在Master-Worker的基礎上又增加了一層Manager進程。這也就解答了我們開頭拋出的問題為什麼是5個行程而不是3個行程了。 (1個Master進程 1個Manager進程 2個Worker進程 1個Task進程)
正所謂“存在即合理”,我們來看看Master\Manager\Worker三種進程各自存在的原因。
Master程序是一個多執行緒程式。註解:按照我們先前的理解,多個執行緒是運行在單一進程的上下文中的,其實對於單一進程中的每一個線程,都有它自己的上下文,但是由於共同存在於同一進程,所以它們也共享這個進程,包括它的程式碼、資料等等。
再回來繼續說Master進程,Master進程就是我們的主進程,掌管生殺大權,它掛了,那底下的都得玩完。 Master進程,包含主線程,多個Reactor線程等。
每一個執行緒都有自己的用途,例如主執行緒用於Accept、訊號處理等操作,而Reactor執行緒是處理tcp連接,處理網路IO,收發資料的執行緒。
說明兩點:
通常,主線程處理完新的連接後,會將這個連接分配給固定的Reactor線程,並且這個Reactor線程會一直負責監聽此socket(上文中後面對socket更新為socket即套接字,是用來與另一個進程進行跨網路通訊的文件,文件可讀可寫),換句話說當此socket可讀時,會讀取數據,並將該請求分配給worker進程,這也解釋了我們在swoole初識講解worker進程內的回調onReceive的第三個參數$fromId的含義;當此socket可寫時,會把數據發送給tcp客戶端。
用一張圖清晰的梳理下
那swoole為啥不能像Nginx一樣,是Master-Worker進程結構的呢? Manager進程是乾啥的?
這個我正準備說。
我們知道,在Master-Worker模型中,Master只有一個,Worker是由父程序Master進程複製出來的,且Worker進程可以有多個。
註解:在linux中,父進程可以透過呼叫fork函數來建立一個新的子進程,子進程是父進程的一個副本,幾乎但不完全相同,二者的最大差異就是都擁有自己獨立的進程ID,即PID。
對於多執行緒的Master進程而言,想要多Worker進程就必須fork操作,但是fork操作是不安全的,所以,在swoole中,有一個專職的Manager進程,Manager進程就專門負責worker/task流程的fork操作與管理。換句話說,對於worker進程的建立、回收等操作全權有「保姆」Manager進程進行管理。
通常,worker进程被误杀或者由于程序的原因会异常退出,Manager进程为了保证服务的稳定性,会重新拉起新的worker进程,意思就是Worker进程你发生意外“死”了,没关系,我自身不“死”,就可以fork千千万万个你。
当然,Master进程和Manager进程我们是不怎么关心的,从前面两篇文章我们了解到,真正实现业务逻辑,是在worker/task进程内完成的。
再来一张图梳理下Manager进程和Worker/Task进程的关系。
再回到我们开篇抛出的的5个进程的问题,ps的结果简直一模一样,有没有办法能区分这5个进程哪个是哪个呢?
有同学要说啦,既然各个进程之间存在父子关系,那我们就可以通过linux的pstree命令查看结果。
$ pstree | grep server-process | | \-+= 02548 manks php server-process.php | | \-+- 02549 manks php server-process.php | | |--- 02550 manks php server-process.php | | |--- 02551 manks php server-process.php | | \--- 02552 manks php server-process.php | \--- 02572 manks grep server-process
注:centos下命令可修改为 pstree -ap | grep server-process
从结果中我们可以看出,进程id等于02548的进程就是Master进程,因为从结构上看就它是“父”嘛,02549是Manager进程,Worker进程和Task进程就是02550、02551和02552了(每个人的电脑上显示的进程id可能不同,但顺序是一致的,依照此模型分析即可)。
我们看到pstree命令也只能得到大致结果,而且在事先不知道的情况下,根本无法区分Worker进程和Task进程。
在swoole中,我们可以在各个进程启动和关闭的回调中去解决上面这个问题。各个进程的启动和关闭?那岂不是又要记住主进程、Manager进程、Worker进程,二三得六,6个回调函数?
是的,不过这6个是最简单也是最好记的,你实际需要了解的可能还要更多。
Master进程: 启动:onStart 关闭:onShutdown Manager进程: 启动:onManagerStart 关闭:onManagerStop Worker进程: 启动:onWorkerStart 关闭:onWorkerStop
提醒:task_worker也会触发onWorkerStart回调。
是不是很好记?那我们就在server-process.php中通过上面这几种回调来实现对各个进程名的修改。
$serv->on("start", function ($serv){ swoole_set_process_name('server-process: master'); }); // 以下回调发生在Manager进程 $serv->on('ManagerStart', function ($serv){ swoole_set_process_name('server-process: manager'); }); $serv->on('WorkerStart', function ($serv, $workerId){ if($workerId >= $serv->setting['worker_num']) { swoole_set_process_name("server-process: task"); } else { swoole_set_process_name("server-process: worker"); } });
注意:因mac下不支持swoole_set_process_name函数,即不能修改进程名,我们换台centos运行下看看结果(实际上你的服务器也不可能是mac)
# ps aux | grep server-process root 27546 xxx... server-process: master root 27547 xxx... server-process: manager root 27549 xxx... server-process: task worker root 27550 xxx... server-process: worker root 27551 xxx... server-process: worker root 27570 xxx... grep --color=auto simple
运行结果谁是谁一目了然,简直了!
有同学傻眼了,说在workerStart回调中写的看不明白,worker进程和task进程怎么区分的?
我来解释一下:在onWorkerStart回调中,$workerId表示的是一个值,这个值的范围是0~worker_num,worker_num是我们的对worker进程的配置,其中0~worker_num表示worker进程的标识,包括0但不包括worker_num;worker_num~worker_num+task_worker_num是task进程的标识,包括worker_num不包括worker_num+task_worker_num。
按照高中学的区间的知识可能更好理解,以我们案例的配置,workerId的值的范围就是[0,2],[0,2)表示worker进程,[2,3)就表示task_worker进程。
swoole的进程模型很重要,本篇掌握不好,后面的理解可能就会有些问题。
我们在onWorkerStart的回调中,用了serv−>setting去获取配置的server信息,在swoole中预留了一些swooleserver的属性,我们可以在回调函数中访问。比如说我们可以用serv−>setting去获取配置的server信息,在swoole中预留了一些swooleserver的属性,我们可以在回调函数中访问。比如说我们可以用serv->connections属性获取当前server的所有的连接,再比如我们可以通过$serv->master_pid属性获取当前server的主进程id等等。
以上是介紹swoole之進程模型的詳細內容。更多資訊請關注PHP中文網其他相關文章!