[TOC]
* * * * *
## 1 源代碼
~~~
public static function runAll()
{
self::checkSapiEnv();
self::init();
self::parseCommand();
self::daemonize();
self::initWorkers();
self::installSignal();
self::saveMasterPid();
self::forkWorkers();
self::displayUI();
self::resetStd();
self::monitorWorkers();
}
~~~
## 2 文件分析
### 0 總體流程
~~~
checkSapiEnv() ;檢查sapi環境是否為cli
init() ;worker環境初始化
parseComand() ;命令行參數解析
daemonize() ;主進程守護模式啟動
initWorkers() ;初始化worker實例
installSignal() ;安裝信號處理句柄
saveMasterPid() ;保存主pid
forkWorkers() ;創建worker子進程
displayUI() ;啟動信息顯示
resetStd() ;重定向輸入輸出
monitorWorkers() ;監控所有worker子進程
~~~
### 1 checkSapiEnv() 檢查sapi環境是否為wcli
~~~
if (php_sapi_name() != "cli") {
exit("only run in command line mode \n");
}
~~~
調用php_sapi_name()檢查sapi運行環境是否為cli
### 2 init() 環境初始化
~~~
$backtrace = debug_backtrace();
self::$_startFile = $backtrace[count($backtrace) - 1]['file'];
~~~
啟動文件
~~~
if (empty(self::$pidFile)) {
self::$pidFile = __DIR__ . "/../" . str_replace('/', '_', self::$_startFile) . ".pid";
}
~~~
進程文件
~~~
if (empty(self::$logFile)) {
self::$logFile = __DIR__ . '/../workerman.log';
}
touch(self::$logFile);
chmod(self::$logFile, 0622);
~~~
日志文件
`self::$_status = self::STATUS_STARTING;`
worker狀態
~~~
self::$_globalStatistics['start_timestamp'] = time();
self::$_statisticsFile = sys_get_temp_dir() . '/workerman.status';
~~~
啟動狀態記錄
`self::setProcessTitle('WorkerMan: master process start_file=' . self::$_startFile);`
設置進程名稱
worker::setProcessTitle() 見框架工具 worker文件
`self::initId();`
初始化workerid數據
worker::setProcessTitle()
`Timer::init();`
計時器初始化
### 3 parseComand() 命令行解析
`global $argv;`
命令行參數
`$start_file = $argv[0];`
啟動文件
~~~
if (!isset($argv[1])) {
exit("Usage: php yourfile.php {start|stop|restart|reload|status|kill}\n");
}
~~~
啟動文件后的信號參數檢查
~~~
$command = trim($argv[1]);
$command2 = isset($argv[2]) ? $argv[2] : '';
~~~
命令參數獲取
~~~
$mode = '';
if ($command === 'start') {
if ($command2 === '-d') {
$mode = 'in DAEMON mode';
} else {
$mode = 'in DEBUG mode';
}
}
~~~
php yourfile.php start debug調試模式啟動
php yourfile.php start -d 守護進程模式啟動
`self::log("Workerman[$start_file] $command $mode");`
日志記錄啟動信息
worker::log()見 框架工具 worker
~~~
$master_pid = @file_get_contents(self::$pidFile);
$master_is_alive = $master_pid && @posix_kill($master_pid, 0);
~~~
主進程id獲取與狀態檢測
posix_kill 見 基礎原理 基礎函數
~~~
if ($master_is_alive) {
if ($command === 'start') {
self::log("Workerman[$start_file] already running");
exit;
}
} elseif ($command !== 'start' && $command !== 'restart') {
self::log("Workerman[$start_file] not run");
}
~~~
主進程啟動檢測
其他命令執行
`switch ($command) {}`
~~~
case 'kill':
exec("ps aux | grep $start_file | grep -v grep | awk '{print $2}' |xargs kill -SIGINT");
exec("ps aux | grep $start_file | grep -v grep | awk '{print $2}' |xargs kill -SIGKILL");
break;
~~~
>[info] kill命令的執行
exec() 見 基礎原理 基礎函數
~~~
case 'start':
if ($command2 === '-d') {
Worker::$daemonize = true;
}
break;
~~~
>[info] start 命令的執行
設置守護進程模式
~~~
case 'status':
if (is_file(self::$_statisticsFile)) {
@unlink(self::$_statisticsFile);
}
posix_kill($master_pid, SIGUSR2);
usleep(100000);
@readfile(self::$_statisticsFile);
exit(0);
~~~
>[info] status命令的執行
刪除狀態文件
發送信號
等待一會
讀取狀態文件
~~~
case 'restart':
case 'stop':
self::log("Workerman[$start_file] is stoping ...");
// Send stop signal to master process.
$master_pid && posix_kill($master_pid, SIGINT);
// Timeout.
$timeout = 5;
$start_time = time();
// Check master process is still alive?
while (1) {
$master_is_alive = $master_pid && posix_kill($master_pid, 0);
if ($master_is_alive) {
// Timeout?
if (time() - $start_time >= $timeout) {
self::log("Workerman[$start_file] stop fail");
exit;
}
// Waiting amoment.
usleep(10000);
continue;
}
// Stop success.
self::log("Workerman[$start_file] stop success");
if ($command === 'stop') {
exit(0);
}
if ($command2 === '-d') {
Worker::$daemonize = true;
}
break;
}
break;
~~~
>[info] restart,stop命令的執行
~~~
self::log("Workerman[$start_file] is stoping ...");
~~~
日志記錄 正在停止信息
~~~
$master_pid && posix_kill($master_pid, SIGINT);
~~~
主進程停止信號
~~~
$timeout = 5;
$start_time = time();
~~~
延遲計時
~~~
$master_is_alive = $master_pid && posix_kill($master_pid, 0);
~~~
~~~
if ($master_is_alive) {
// Timeout?
if (time() - $start_time >= $timeout) {
self::log("Workerman[$start_file] stop fail");
exit;
}
// Waiting amoment.
usleep(10000);
continue;
}
~~~
檢測是否停止成功
~~~
self::log("Workerman[$start_file] stop success");
~~~
日志記錄停止成功
~~~
if ($command === 'stop') {
exit(0);
}
~~~
stop命令 直接退出
~~~
if ($command2 === '-d') {
Worker::$daemonize = true;
}
break;
~~~
restart -d 命令 設置守護模式
~~~
case 'reload':
posix_kill($master_pid, SIGUSR1);
self::log("Workerman[$start_file] reload");
exit;
~~~
>[info] reload 命令
~~~
default :
exit("Usage: php yourfile.php {start|stop|restart|reload|status|kill}\n");
~~~
提示 命令行格式信息
### 4 daemonize() 以守護模式啟動
~~~
if (!self::$daemonize) {
return;
}
~~~
檢測是否需要守護模式啟動
`umask(0);`
修改mask
~~~
$pid = pcntl_fork();
if (-1 === $pid) {
throw new Exception('fork fail');
} elseif ($pid > 0) {
exit(0);
}
~~~
創建進程
pcntl_fork() 見 基礎原理 基礎函數
~~~
if (-1 === posix_setsid()) {
throw new Exception("setsid fail");
}
~~~
session初始化
posix_setsid() 見 基礎原理 基礎函數
~~~
$pid = pcntl_fork();
if (-1 === $pid) {
throw new Exception("fork fail");
} elseif (0 !== $pid) {
exit(0);
}
~~~
??
### 5 initWorkers() 初始化所有worker實例
`foreach (self::$_workers as $worker) {}`
遍歷worker實例數組
~~~
if (empty($worker->name)) {
$worker->name = 'none';
}
~~~
獲取worker名稱
~~~
$worker_name_length = strlen($worker->name);
if (self::$_maxWorkerNameLength < $worker_name_length) {
self::$_maxWorkerNameLength = $worker_name_length;
}
$socket_name_length = strlen($worker->getSocketName());
if (self::$_maxSocketNameLength < $socket_name_length) {
self::$_maxSocketNameLength = $socket_name_length;
}
~~~
名稱長度檢測
~~~
if (empty($worker->user)) {
$worker->user = self::getCurrentUser();
} else {
if (posix_getuid() !== 0 && $worker->user != self::getCurrentUser()) {
self::log('Warning: You must have the root privileges to change uid and gid.');
}
}
~~~
獲取用戶信息
~~~
$user_name_length = strlen($worker->user);
if (self::$_maxUserNameLength < $user_name_length) {
self::$_maxUserNameLength = $user_name_length;
}
~~~
用戶名稱長度檢測
~~~
if (!$worker->reusePort) {
$worker->listen();
}
~~~
開啟監聽端口
worker::listen() 見 框架工具的 worker類
### 6 installSignal() 安裝主進程信號處理函數
~~~
pcntl_signal(SIGINT, array('\Workerman\Worker', 'signalHandler'), false);
pcntl_signal(SIGUSR1, array('\Workerman\Worker', 'signalHandler'), false);
pcntl_signal(SIGUSR2, array('\Workerman\Worker', 'signalHandler'), false);
pcntl_signal(SIGPIPE, SIG_IGN, false);
~~~
stop,reload,status,ignore信號接口函數注冊為worker::signalHanlder()
signalHanlder() 見 框架工具 worker類
### 7 saveMasterPid() 保存主進程id到pid文件
~~~
self::$_masterPid = posix_getpid();
~~~
獲取pid信息
~~~
if (false === @file_put_contents(self::$pidFile, self::$_masterPid)) {
throw new Exception('can not save pid to ' . self::$pidFile);
}
~~~
寫入pid信息
### 8 forkWorkers() 創建workers子進程
~~~
foreach (self::$_workers as $worker) {}
~~~
遍歷workers數組
~~~
if (self::$_status === self::STATUS_STARTING) {
if (empty($worker->name)) {
$worker->name = $worker->getSocketName();
}
$worker_name_length = strlen($worker->name);
if (self::$_maxWorkerNameLength < $worker_name_length) {
self::$_maxWorkerNameLength = $worker_name_length;
}
}
~~~
worker名稱獲取
~~~
while (count(self::$_pidMap[$worker->workerId]) < $worker->count) {
static::forkOneWorker($worker);
}
~~~
依次創建worker進程
forkOneWorker() 見 Worker功能函數
### 9 displayUI() 輸出服務器啟動信息
### 10 resetStd() 重定向標準輸出
設置標準輸出到指定輸出文件,
### 11 monitorWorkers() 監控worker進程
`self::$_status = self::STATUS_RUNNING;`
設置啟動狀態
`pcntl_signal_dispatch();`
根據信號調用相關
~~~
$status = 0;
$pid = pcntl_wait($status, WUNTRACED);
~~~
等待子進程退出信號
`pcntl_signal_dispatch();`
根據信號調用相關
`if ($pid > 0) {}`
子進程退出處理
~~~
else {
// If shutdown state and all child processes exited then master process exit.
if (self::$_status === self::STATUS_SHUTDOWN && !self::getAllWorkerPids()) {
self::exitAndClearAll();
}
}
~~~
所有子進程退出后清理
## 3 總結
worker啟動過程,完成啟動主流程。
>[info] 1 環境檢測與初始化
~~~
self::checkSapiEnv();
self::init();
~~~
>[info] 2 命令行解析
`self::parseCommand();`
>[info] 3 主進程創建與運行
~~~
self::daemonize();
self::initWorkers();
self::installSignal();
self::saveMasterPid();
~~~
>[info] 4 子進程創建與啟動
`self::forkWorkers();`
>[info] 5 監控子進程信號
~~~
self::displayUI();
self::resetStd();
self::monitorWorkers();
~~~