workermanソース-workerman起動プロセス
前にコードについてworkermanの初期化プロセスを見ました.しかし、ポートの傍受方法については.等の操作が具体的に実現する.今度は見てみましょう.workermanはポートをリスニングして実行する方法です.
runAll
前に方法を初期化した後、runAll方法を実行し始めた.runAllメソッドには、以下のいくつかの方法がある.私たちは一つの方法で紹介します.注意ここでは、すべてstaticで実行されます.だから、Workerクラス自体です.
checkSapiEnv
現在のコマンドラインの環境を確認します.なぜこの関数があるのかは、主にwindowの下にあるからです.1つのプロセスしか開くことができません.だから多くの地方でwindowsに対して特殊な処理を行う必要がある.コードを見てみましょう
init
ここでは主にエラー処理,pidファイル,ログファイルの初期化を行う.初期化タイマーもあります
その中で最も主要なのはworkerのidmapを初期化し、workerの対応関係を記録することである.ここでは主にサブワークのpid情報を記憶する.関数を見てみましょう
初期化が完了すると、worker_id=>サブプロセス情報.たとえば
このような構成は、idを対応する情報に対応することを容易にする.
lock
なぜ起動ファイルをロックする必要があるのか.同じ時間にworkerを起動すると問題が発生します.これにより、予知できないエラーが発生する.ここで起動ファイルをロックします.保存する原子性操作について
parseCommand
ファイルのロックが完了すると、コマンドの解析が開始されます.今回は情報が多いため.単独で文章を書いて解読します.
daemonize
daemonizeがバックグラウンドで実行するかどうか、コマンドラインで-dパラメータを使用すると、この関数が実行されます.しかし、私たちが実行するコマンドラインに-d.が追加されていないため、この関数は実行されません.ちょっと見てみましょう.
initWorkers
バックグラウンドの実行が完了すると、workerの初期化が開始されます.
installSignal
workerの初期化が完了すると、信号セットが傍受される.ここで傍受する信号セット.信号セットはlinuxでのみ使用できる.
saveMasterPid
メインプロセスのIDをpidFileファイルに保存する.
unlock
以上の実行が完了すると、起動ファイルのロック状態が解除する.この関数は主にロック解除操作を行う.
displayUI
uiを表示します.workermanは処理する時、先に展示したuiです.次のような情報を印刷することです
したがって、情報が印刷する後も、必ずしもイベントを正しく傍受するとは限らない.次に、引き続き確認します.
forkWorkers
以上の準備が完了すると、forkのサブプロセスが始まります.サブプロセスのリスニングを設定します.
プラットフォームに基づいて対応するサブプロセスを処理する.私たちはlinuxにいるのでstatic::forkWorkersForLinuxという関数を見てみましょう.
static::forOneWorkerForLinux関数は主にworker情報を処理する.ここで見てみましょう.
上記のコードがlinuxで実行すると、メインプロセスとサブプロセスに分けて処理する.メインプロセスに含まれるすべてのサブプロセス情報の保存.サブプロセスはここでメソッドを傍受する.サブプロセスでは、すべての親プロセスに対する変数が共有されていることに注意してください.サブプロセスがイベントをどのように監視しているかを見てみましょう.$worker->run();
run関数はworkerStartイベントのトリガを開始し、workerイベントのトリガを待つ.ここのglobalEventは私たちのイベント処理関数です以上がサブプロセスで完了する処理である.サブプロセスはここで停止します.
resetStd
forkがすべてのサブプロセスを完了すると、リダイレクト入力出力が実行されます.
monitorWorkers()
最後に、私たちのサブプロセス情報を監視します.ここは私たちの紙幅が長すぎるので、もう1枚書きます.
未完了
runAll
前に方法を初期化した後、runAll方法を実行し始めた.runAllメソッドには、以下のいくつかの方法がある.私たちは一つの方法で紹介します.注意ここでは、すべてstaticで実行されます.だから、Workerクラス自体です.
// . linux.
static::checkSapiEnv();
//
static::init();
//
static::lock();
//
static::parseCommand();
//
static::daemonize();
// worker
static::initWorkers();
//
static::installSignal();
// ID
static::saveMasterPid();
//
static::unlock();
// ui
static::displayUI();
// fork
static::forkWorkers();
//
static::resetStd();
// .
static::monitorWorkers();
checkSapiEnv
現在のコマンドラインの環境を確認します.なぜこの関数があるのかは、主にwindowの下にあるからです.1つのプロセスしか開くことができません.だから多くの地方でwindowsに対して特殊な処理を行う必要がある.コードを見てみましょう
// cli , .
if (php_sapi_name() != "cli") {
exit("only run in command line mode n");
}
// ,windows ,linux /. .
if (DIRECTORY_SEPARATOR === '') {
self::$_OS = OS_TYPE_WINDOWS;
}
init
ここでは主にエラー処理,pidファイル,ログファイルの初期化を行う.初期化タイマーもあります
set_error_handler(function($code, $msg, $file, $line){
Worker::safeEcho("$msg in file $file on line $linen");
});
// . .
$backtrace = debug_backtrace();
// startFile . /var/www/study/https.php, startFile /var/www/study/https.php.
static::$_startFile = $backtrace[count($backtrace) - 1]['file'];
// . startFile .
$unique_prefix = str_replace('/', '_', static::$_startFile);
// pidFile. vendor/workerman .
if (empty(static::$pidFile)) {
static::$pidFile = __DIR__ . "/../$unique_prefix.pid";
}
// .
if (empty(static::$logFile)) {
static::$logFile = __DIR__ . '/../workerman.log';
}
$log_file = (string)static::$logFile;
// , .
if (!is_file($log_file)) {
touch($log_file);
chmod($log_file, 0622);
}
// ...
static::$_status = static::STATUS_STARTING;
//
static::$_globalStatistics['start_timestamp'] = time();
static::$_statisticsFile = sys_get_temp_dir() . "/$unique_prefix.status";
//
static::setProcessTitle('WorkerMan: master process start_file=' . static::$_startFile);
// worker idmap.
static::initId();
// .
Timer::init();
その中で最も主要なのはworkerのidmapを初期化し、workerの対応関係を記録することである.ここでは主にサブワークのpid情報を記憶する.関数を見てみましょう
protected static function initId()
{
// worker, .
foreach (static::$_workers as $worker_id => $worker){
$new_id_map = array();
$worker->count = $worker->count <= 0 ? 1 : $worker->count;
for($key = 0; $key < $worker->count; $key++) {
$new_id_map[$key] = isset(static::$_idMap[$worker_id][$key]) ? static::$_idMap[$worker_id][$key] : 0;
}
static::$_idMap[$worker_id] = $new_id_map;
}
}
初期化が完了すると、worker_id=>サブプロセス情報.たとえば
array(1) {
["000000001364d94e00000000462a38e7"]=> array(4) {
[0]=> int(0)
[1]=> int(0)
[2]=> int(0)
[3]=> int(0)
}
}
このような構成は、idを対応する情報に対応することを容易にする.
lock
protected static function lock()
{
// .
$fd = fopen(static::$_startFile, 'r'); // . workerman .
if (!$fd || !flock($fd, LOCK_EX)) {
static::log("Workerman[".static::$_startFile."] already running"); exit;
}
}
なぜ起動ファイルをロックする必要があるのか.同じ時間にworkerを起動すると問題が発生します.これにより、予知できないエラーが発生する.ここで起動ファイルをロックします.保存する原子性操作について
parseCommand
ファイルのロックが完了すると、コマンドの解析が開始されます.今回は情報が多いため.単独で文章を書いて解読します.
daemonize
daemonizeがバックグラウンドで実行するかどうか、コマンドラインで-dパラメータを使用すると、この関数が実行されます.しかし、私たちが実行するコマンドラインに-d.が追加されていないため、この関数は実行されません.ちょっと見てみましょう.
protected static function daemonize()
{
// , linux. .
if (!static::$daemonize || static::$_OS !== OS_TYPE_LINUX) {
return;
}
// umask.
umask(0); // , pid. ,pid -1. pid 0.
$pid = pcntl_fork();
if (-1 === $pid) {
throw new Exception('fork fail');
} elseif ($pid > 0) { exit(0); }
// , . , 1 , init .
if (-1 === posix_setsid()) {
throw new Exception("setsid fail");
}
// . SVR4 .
$pid = pcntl_fork();
if (-1 === $pid) {
throw new Exception("fork fail"); } elseif (0 !== $pid) { exit(0);
}
}
initWorkers
バックグラウンドの実行が完了すると、workerの初期化が開始されます.
protected static function initWorkers()
{
// linux .
if (static::$_OS !== OS_TYPE_LINUX) {
return;
}
// worker
foreach (static::$_workers as $worker) {
// worker name.
if (empty($worker->name)) {
$worker->name = 'none';
}
// worker user
if (empty($worker->user)) {
$worker->user = static::getCurrentUser();
} else {
if (posix_getuid() !== 0 && $worker->user != static::getCurrentUser()) {
static::log('Warning: You must have the root privileges to change uid and gid.');
}
}
// socket .
$worker->socket = $worker->getSocketName();
// .
$worker->status = ' [OK] ';
// ui.
foreach(static::getUiColumns() as $column_name => $prop){
!isset($worker->{$prop}) && $worker->{$prop}= 'NNNN';
$prop_length = strlen($worker->{$prop});
$key = '_max' . ucfirst(strtolower($column_name)) . 'NameLength';
static::$$key = max(static::$$key, $prop_length);
}
// .
if (!$worker->reusePort) { $worker->listen(); } }}
installSignal
workerの初期化が完了すると、信号セットが傍受される.ここで傍受する信号セット.信号セットはlinuxでのみ使用できる.
protected static function installSignal()
{
if (static::$_OS !== OS_TYPE_LINUX) {
return;
}
// ,Ctrl+c
pcntl_signal(SIGINT, array('WorkermanWorker', 'signalHandler'), false);
// , kill. .
pcntl_signal(SIGTERM, array('WorkermanWorker', 'signalHandler'), false);
//
pcntl_signal(SIGUSR1, array('WorkermanWorker', 'signalHandler'), false);
//
pcntl_signal(SIGQUIT, array('WorkermanWorker', 'signalHandler'), false);
//
pcntl_signal(SIGUSR2, array('WorkermanWorker', 'signalHandler'), false);
//
pcntl_signal(SIGIO, array('WorkermanWorker', 'signalHandler'), false);
// SIGPIPE
pcntl_signal(SIGPIPE, SIG_IGN, false);
}
saveMasterPid
メインプロセスのIDをpidFileファイルに保存する.
protected static function saveMasterPid()
{
if (static::$_OS !== OS_TYPE_LINUX) {
return;
}
static::$_masterPid = posix_getpid(); // ID pidFile ,pidFile init vendor/workerman/ pid .
if (false === file_put_contents(static::$pidFile, static::$_masterPid)) {
throw new Exception('can not save pid to ' . static::$pidFile);
}
}
unlock
以上の実行が完了すると、起動ファイルのロック状態が解除する.この関数は主にロック解除操作を行う.
protected static function unlock()
{
$fd = fopen(static::$_startFile, 'r'); $fd && flock($fd, LOCK_UN);
}
displayUI
uiを表示します.workermanは処理する時、先に展示したuiです.次のような情報を印刷することです
Workerman[http.php] start in DEBUG mode
----------------------------------------- WORKERMAN -----------------------------------------
Workerman version:3.5.22 PHP version:7.1.23-4+ubuntu18.04.1+deb.sury.org+1
------------------------------------------ WORKERS ------------------------------------------
proto user worker listen processes status
tcp adolph none http://0.0.0.0:2345 4 [OK]
---------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.
したがって、情報が印刷する後も、必ずしもイベントを正しく傍受するとは限らない.次に、引き続き確認します.
forkWorkers
以上の準備が完了すると、forkのサブプロセスが始まります.サブプロセスのリスニングを設定します.
rotected static function forkWorkers()
{
if (static::$_OS === OS_TYPE_LINUX) {
// linux fork
static::forkWorkersForLinux();
} else {
// windows
static::forkWorkersForWindows();
}
}
プラットフォームに基づいて対応するサブプロセスを処理する.私たちはlinuxにいるのでstatic::forkWorkersForLinuxという関数を見てみましょう.
protected static function forkWorkersForLinux()
{
// worker, init .
foreach (static::$_workers as $worker) {
if (static::$_status === static::STATUS_STARTING) {
if (empty($worker->name)) {
$worker->name = $worker->getSocketName();
}
$worker_name_length = strlen($worker->name);
if (static::$_maxWorkerNameLength < $worker_name_length) {
static::$_maxWorkerNameLength = $worker_name_length;
}
}
// fork. $_pidMap . $_pidMap workerId => .
// _pidMap new Worker Worker . .
while (count(static::$_pidMap[$worker->workerId]) < $worker->count) {
// fork
static::forkOneWorkerForLinux($worker);
}
}
}
static::forOneWorkerForLinux関数は主にworker情報を処理する.ここで見てみましょう.
protected static function forkOneWorkerForLinux($worker)
{
// id . idMap
// array_search($pid, static::$_idMap[$worker_id]);
// idMap workerId=> . , id 0.
$id = static::getId($worker->workerId, 0);
if ($id === false) {
return;
}
// fork
$pid = pcntl_fork();
// pid 0. .
if ($pid > 0) {
// pidMap ,
static::$_pidMap[$worker->workerId][$pid] = $pid;
// idMap id . .
static::$_idMap[$worker->workerId][$id] = $pid;
} elseif (0 === $pid) {
// .
srand();
mt_srand();
// .
if ($worker->reusePort) {
$worker->listen();
}
// STATUS_STARTING, .
if (static::$_status === static::STATUS_STARTING) {
static::resetStd();
}
// pid. , . pidMap .
static::$_pidMap = array();
// worker . , worker .
foreach(static::$_workers as $key => $one_worker) {
if ($one_worker->workerId !== $worker->workerId) {
$one_worker->unlisten(); unset(static::$_workers[$key]);
}
}
// .
Timer::delAll();
static::setProcessTitle('WorkerMan: worker process ' . $worker->name . ' ' . $worker->getSocketName());
// .
$worker->setUserAndGroup();
$worker->id = $id;
// run .
$worker->run();
$err = new Exception('event-loop exited');
static::log($err);
exit(250);
} else {
throw new Exception("forkOneWorker fail");
}
}
上記のコードがlinuxで実行すると、メインプロセスとサブプロセスに分けて処理する.メインプロセスに含まれるすべてのサブプロセス情報の保存.サブプロセスはここでメソッドを傍受する.サブプロセスでは、すべての親プロセスに対する変数が共有されていることに注意してください.サブプロセスがイベントをどのように監視しているかを見てみましょう.$worker->run();
public function run()
{
// .
static::$_status = static::STATUS_RUNNING;
// .
register_shutdown_function(array("WorkermanWorker", 'checkErrors'));
// root
Autoloader::setRootPath($this->_autoloadRootPath);
//
if (!static::$globalEvent) {
$event_loop_class = static::getEventLoopName();
static::$globalEvent = new $event_loop_class; $this->resumeAccept();
}
// .
static::reinstallSignal();
//
Timer::init(static::$globalEvent);
// .
if (empty($this->onMessage)) {
$this->onMessage = function () {};
}
// .
restore_error_handler();
// workerStart .
if ($this->onWorkerStart) {
try {
call_user_func($this->onWorkerStart, $this);
} catch (Exception $e) {
static::log($e);
// Avoid rapid infinite loop exit.
sleep(1);
exit(250);
} catch (Error $e) {
static::log($e);
// Avoid rapid infinite loop exit.
sleep(1);
exit(250);
}
}
// .
static::$globalEvent->loop();
}
run関数はworkerStartイベントのトリガを開始し、workerイベントのトリガを待つ.ここのglobalEventは私たちのイベント処理関数です以上がサブプロセスで完了する処理である.サブプロセスはここで停止します.
resetStd
forkがすべてのサブプロセスを完了すると、リダイレクト入力出力が実行されます.
public static function resetStd()
{
// .
if (!static::$daemonize || static::$_OS !== OS_TYPE_LINUX) {
return;
}
global $STDOUT, $STDERR;
// .
$handle = fopen(static::$stdoutFile, "a");
if ($handle) {
unset($handle);
set_error_handler(function(){});
fclose($STDOUT);
fclose($STDERR);
fclose(STDOUT);
fclose(STDERR);
$STDOUT = fopen(static::$stdoutFile, "a");
$STDERR = fopen(static::$stdoutFile, "a");
// .
static::outputStream($STDOUT);
restoe_error_handler();
} else {
throw new Exception('can not open stdoutFile ' . static::$stdoutFile);
}
}
monitorWorkers()
最後に、私たちのサブプロセス情報を監視します.ここは私たちの紙幅が長すぎるので、もう1枚書きます.
未完了
workerman
workerman