ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[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(); ~~~