這篇文章給大家分享的是有關(guān)workerman源碼分析之啟動(dòng)過(guò)程詳解的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧。

workerman
版本:3.1.8(linux)
模型:GatewayWorker(Worker模型可與之類比)
注:只貼出講解部分代碼,出處以文件名形式給出,大家可自行查看
workerman最初只開發(fā)了Linux版本,win是后來(lái)增加的,基于命令行模式運(yùn)行(cli)。
多進(jìn)程模型
工作進(jìn)程,Master、Gateway和Worker,Gateway主要用于處理IO事件,保存客戶端鏈接狀態(tài),將數(shù)據(jù)處理請(qǐng)求發(fā)送給Worker等工作,Worker則是完全的業(yè)務(wù)邏輯處理,前者為IO密集型,后者為計(jì)算密集型,它們之間通過(guò)網(wǎng)絡(luò)通信,Gateway和Worker兩兩間注冊(cè)通信地址,所以非常方便的進(jìn)行分布式部署,如果業(yè)務(wù)處理量大可以單純的增加Worker服務(wù)。

它們有一個(gè)負(fù)責(zé)監(jiān)聽的父進(jìn)程(Master),監(jiān)聽子進(jìn)程狀態(tài),發(fā)送 signal 給子進(jìn)程,接受來(lái)自終端的命令、信號(hào)等工作。父進(jìn)程可以說(shuō)是整個(gè)系統(tǒng)啟動(dòng)后的入口。
啟動(dòng)命令解析
既然以命令模式(cli)運(yùn)行(注意與 fpm 的區(qū)別,后者處理來(lái)自網(wǎng)頁(yè)端的請(qǐng)求),就必然有一個(gè)啟動(dòng)腳本解析命令,譬如說(shuō)3.x版本(之前默認(rèn)為daemon)新增一個(gè) -d 參數(shù),以表示守護(hù)進(jìn)程運(yùn)行,解析到該參數(shù)設(shè)置 self::$daemon = true, 隨后fork子進(jìn)程以脫離當(dāng)前進(jìn)程組,設(shè)置進(jìn)程組組長(zhǎng)等工作。
這里有兩個(gè)非常重要的參數(shù) $argc 和 $argc,前者表示參數(shù)個(gè)數(shù),后者為一個(gè)數(shù)組,保存有命令的所有參數(shù),比如:sudo php start.php start -d,$argv就是 array( [0]=>start.php, [1]=>start, [2]=>-d ),而解析主要用到$argv。
啟動(dòng)主要執(zhí)行下面步驟:
1、包含自動(dòng)加載器 Autoloader ,加載各 Application 下啟動(dòng)文件;
2、設(shè)置 _appInitPath 根目錄;
3、解析,初始化參數(shù),執(zhí)行相應(yīng)命令。
下面是具體實(shí)現(xiàn)):
public static function parseCommand()
{
// 檢查運(yùn)行命令的參數(shù)
global $argv;
$start_file = $argv[0];
// 命令
$command = trim($argv[1]);
// 子命令,目前只支持-d
$command2 = isset($argv[2]) ? $argv[2] : '';
// 檢查主進(jìn)程是否在運(yùn)行
$master_pid = @file_get_contents(self::$pidFile);
$master_is_alive = $master_pid && @posix_kill($master_pid, 0);
if($master_is_alive)
{
if($command === 'start')
{
self::log("Workerman[$start_file] is running");
}
}
elseif($command !== 'start' && $command !== 'restart')
{
self::log("Workerman[$start_file] not run");
}
// 根據(jù)命令做相應(yīng)處理
switch($command)
{
// 啟動(dòng) workerman
case 'start':
if($command2 === '-d')
{
Worker::$daemonize = true;
}
break;
// 顯示 workerman 運(yùn)行狀態(tài)
case 'status':
exit(0);
// 重啟 workerman
case 'restart':
// 停止 workeran
case 'stop':
// 想主進(jìn)程發(fā)送SIGINT信號(hào),主進(jìn)程會(huì)向所有子進(jìn)程發(fā)送SIGINT信號(hào)
$master_pid && posix_kill($master_pid, SIGINT);
// 如果 $timeout 秒后主進(jìn)程沒(méi)有退出則展示失敗界面
$timeout = 5;
$start_time = time();
while(1)
{
// 檢查主進(jìn)程是否存活
$master_is_alive = $master_pid && posix_kill($master_pid, 0);
if($master_is_alive)
{
// 檢查是否超過(guò)$timeout時(shí)間
if(time() - $start_time >= $timeout)
{
self::log("Workerman[$start_file] stop fail");
exit;
}
usleep(10000);
continue;
}
self::log("Workerman[$start_file] stop success");
// 是restart命令
if($command === 'stop')
{
exit(0);
}
// -d 說(shuō)明是以守護(hù)進(jìn)程的方式啟動(dòng)
if($command2 === '-d')
{
Worker::$daemonize = true;
}
break;
}
break;
// 平滑重啟 workerman
case 'reload':
exit;
}
}walker代碼注釋已經(jīng)非常詳盡,下面有幾點(diǎn)細(xì)節(jié)處:
1、檢查主進(jìn)程是否存活:17行的邏輯與操作,如果主進(jìn)程PID存在情況下,向該進(jìn)程發(fā)送信號(hào)0,實(shí)際上并沒(méi)有發(fā)送任何信息,只是檢測(cè)該進(jìn)程(或進(jìn)程組)是否存活,同時(shí)也檢測(cè)當(dāng)前用戶是否有權(quán)限發(fā)送系統(tǒng)信號(hào);
2、為什么主進(jìn)程PID會(huì)保存?系統(tǒng)啟動(dòng)后脫離當(dāng)前terminal運(yùn)行,如果要執(zhí)行關(guān)閉或者其他命令,此時(shí)是以另外的一個(gè)進(jìn)程執(zhí)行該命令,如果我們連進(jìn)程PID都不知道,那該向誰(shuí)發(fā)信號(hào)呢?
所以主進(jìn)程PID必須保存起來(lái),而且主進(jìn)程負(fù)責(zé)監(jiān)聽其他子進(jìn)程,所以它是我們繼續(xù)操作的入口。
Worker::runAll()
php的socket編程其實(shí)和C差不多,后者對(duì)socket進(jìn)行了再包裹,并提供接口給php,在php下網(wǎng)絡(luò)編程步驟大大減少。
譬如:stream_socket_server 和 stream_socket_client 直接創(chuàng)建了server/client socke(php有兩套socket操作函數(shù))。wm則大量使用了前者,啟動(dòng)過(guò)程如下(注釋已經(jīng)非常詳盡):
public static function runAll()
{
// 初始化環(huán)境變量
self::init();
// 解析命令
self::parseCommand();
// 嘗試以守護(hù)進(jìn)程模式運(yùn)行
self::daemonize();
// 初始化所有worker實(shí)例,主要是監(jiān)聽端口
self::initWorkers();
// 初始化所有信號(hào)處理函數(shù)
self::installSignal();
// 保存主進(jìn)程pid
self::saveMasterPid();
// 創(chuàng)建子進(jìn)程(worker進(jìn)程)并運(yùn)行
self::forkWorkers();
// 展示啟動(dòng)界面
self::displayUI();
// 嘗試重定向標(biāo)準(zhǔn)輸入輸出
self::resetStd();
// 監(jiān)控所有子進(jìn)程(worker進(jìn)程)
self::monitorWorkers();
}下面還是只說(shuō)該過(guò)程的關(guān)鍵點(diǎn):
1、始化環(huán)境變量,例如設(shè)置主進(jìn)程名稱、日志路徑,初始化定時(shí)器等等;
2、解析命令行參數(shù),主要用到 $argc 和 $argc 用法同C語(yǔ)言;
3、生成守護(hù)進(jìn)程,以脫離當(dāng)前終端(兩年前大部分認(rèn)為PHP無(wú)法做daemon,其實(shí)這是個(gè)誤區(qū)!其實(shí)PHP在linux的進(jìn)程模型很穩(wěn)定,現(xiàn)在wm在商業(yè)的應(yīng)用已經(jīng)非常成熟,國(guó)內(nèi)某公司每天處理幾億的連接,用于訂單、支付調(diào)用,大家可以打消顧慮了);
4、初始化所有worker實(shí)例(注意,這里是在主進(jìn)程做的,只是生成了一堆 server 并沒(méi)有設(shè)置監(jiān)聽,多進(jìn)程模型是在子進(jìn)程做的監(jiān)聽,即IO復(fù)用);
5、為主進(jìn)程注冊(cè)信號(hào)處理函數(shù);
6、保存主進(jìn)程PID,當(dāng)系統(tǒng)運(yùn)行后,我們?cè)诮K端查看系統(tǒng)狀態(tài)或者執(zhí)行關(guān)閉、重啟命令,是通過(guò)主進(jìn)程進(jìn)行通信,所以需要知道主進(jìn)程PID,我們知道在終端下敲入一個(gè)可執(zhí)行命令,實(shí)則是在當(dāng)前終端下新建一個(gè)子進(jìn)程來(lái)執(zhí)行,所以我們需要得知主進(jìn)程PID,以向WM主進(jìn)程發(fā)送SIGNAL,這時(shí)信號(hào)處理函數(shù)捕獲該信號(hào),并通過(guò)回調(diào)方式執(zhí)行。
7、創(chuàng)建子進(jìn)程,設(shè)置當(dāng)前進(jìn)程用戶(root)。在多進(jìn)程模型中,兩類子進(jìn)程,分別監(jiān)聽不同的server地址,我們?cè)谥鬟M(jìn)程只是創(chuàng)建server并沒(méi)有設(shè)置監(jiān)聽,也沒(méi)有生成指定數(shù)目的server。
原因在于,我們?cè)谝粋€(gè)進(jìn)程多次創(chuàng)建同一個(gè) socket,會(huì)報(bào)錯(cuò), worker數(shù)目其實(shí)就是 socket 數(shù)量,也就是該 socket 的子進(jìn)程數(shù)目,子進(jìn)程繼承了父進(jìn)程上下文,但是只監(jiān)聽特定的 socket 事件;
8、在子進(jìn)程中,將 server socket 注冊(cè)監(jiān)聽事件,用到一個(gè)擴(kuò)展Event,可以實(shí)現(xiàn)IO復(fù)用,并注冊(cè)數(shù)據(jù)讀取回調(diào),同時(shí)也可注冊(cè)socket連接事件回調(diào);
9、輸入輸出重定向;
10、主進(jìn)程監(jiān)聽子進(jìn)程狀態(tài),在一個(gè)無(wú)限循環(huán)中調(diào)用 pcntl_signal_dispatch() 函數(shù),用于捕獲子進(jìn)程退出狀態(tài),該函數(shù)會(huì)一直阻塞,直到有子進(jìn)程退出時(shí)才觸發(fā);
感謝各位的閱讀!關(guān)于“workerman源碼分析之啟動(dòng)過(guò)程詳解”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
本文名稱:workerman源碼分析之啟動(dòng)過(guò)程詳解-創(chuàng)新互聯(lián)
分享網(wǎng)址:http://chinadenli.net/article38/dijopp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供動(dòng)態(tài)網(wǎng)站、網(wǎng)站改版、企業(yè)建站、面包屑導(dǎo)航、做網(wǎng)站、網(wǎng)站收錄
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容