[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();
~~~