/** * 解析启动模式参数 * @param $opt */ static public function params_s($opt) { //判断传入了s参数但是值,则提示错误 if ((isset($opt["s"]) && !$opt["s"]) || (isset($opt["s"]) && !in_array($opt["s"], array("start", "stop", "restart")))) { Main::log_write("Please run: path/to/php main.php -s [start|stop|restart]"); } if (isset($opt["s"]) && in_array($opt["s"], array("start", "stop", "restart"))) { switch ($opt["s"]) { case "start": Crontab::start(); break; case "stop": Crontab::stop(); break; case "restart": Crontab::restart(); break; } } }
这里我们先分析Crontab::start()
从这个意思我们应该知道这应该是进程启动的方法
因为crontab自己注册了引入方法,所以方法在include/Crontab.class.php中
/** * 启动 */ static public function start() { if (file_exists(self::$pid_file)) { die("Pid文件已存在! "); } self::daemon(); self::set_process_name(); self::run(); Main::log_write("启动成功"); }
先检查pid文件是否存在,如果存在说明进程已经启动不能再次开始
self::daemon(); 在上一节,如果params_d($opt) 开启守护进程会执行swoole_process::daemon();//传送门:https://wiki.swoole.com/wiki/page/273.html
self::set_process_name();//设置进程名字 这里没有支持用户自己命名,名字为:lzm_Master
接下来执行run方法:
/** * 运行 */ static protected function run() { self::$tasksHandle = new LoadTasks(strtolower(self::$taskType), self::$taskParams); self::register_signal(); if (self::$checktime) { $run = true; Main::log_write("正在启动..."); while ($run) { $s = date("s"); if ($s == 0) { Crontab::load_config(); self::register_timer(); $run = false; } else { Main::log_write("启动倒计时 " . (60 - $s) . " 秒"); sleep(1); } } } else { self::load_config(); self::register_timer(); } self::get_pid(); self::write_pid(); //开启worker if (self::$worker) { (new Worker())->loadWorker(); } }
106行:self::$tasksHandle = new LoadTasks(strtolower(self::$taskType), self::$taskParams);//这里我们就分析LoadTasksByFile,这里放到下一讲
107行:self::register_signal();//注册信号,用来处理进程退出
如果在客户端进行ctrl+c退出的话,会发送SIGINT信号,但是这里没做处理,那么请不要在客户端直接ctrl+c退出程序,不然的话下次就需要执行重启命令了,因为不退出
如果受到退出信号SIGTERM就会直接调用exit2p:删除掉pid文件然后直接exit退出
还需要处理当子进程退出,为了不让程序变成僵尸进程需要进行wait处理
这里是先判断是否在tasklist中pid是否存在,如果存在这个任务,则直接将子进程关闭,如果这个任务还有unique_list值(这里是任务里的排他数量的意思)会进行自减,杀死后扣除1,如果在load_config中会继续往taskTabel插入任务,争用这个unique字段
如果是工作进程,那么复制一份work进程
至于SIGUSR1信号,这里本来是应该是重新加载配置文件的,不知道为什么这里没有加上实现
/** * 注册信号 */ static private function register_signal() { swoole_process::signal(SIGTERM, function ($signo) { self::exit2p("收到退出信号,退出主进程"); }); swoole_process::signal(SIGCHLD, function ($signo) { while ($ret = swoole_process::wait(false)) { $pid = $ret['pid']; if (isset(self::$task_list[$pid])) { $task = self::$task_list[$pid]; if ($task["type"] == "crontab") { $end = microtime(true); $start = $task["start"]; $id = $task["id"]; Main::log_write("{$id} [Runtime:" . sprintf("%0.6f", $end - $start) . "]"); $task["process"]->close();//关闭进程 unset(self::$task_list[$pid]); if (isset(self::$unique_list[$id]) && self::$unique_list[$id] > 0) { self::$unique_list[$id]--; } } if ($task["type"] == "worker") { $end = microtime(true); $start = $task["start"]; $classname = $task["classname"]; Main::log_write("{$classname}_{$task["number"]} [Runtime:" . sprintf("%0.6f", $end - $start) . "]"); $task["process"]->close();//关闭进程 (new Worker())->create_process($classname, $task["number"], $task["redis"]); } } }; }); swoole_process::signal(SIGUSR1, function ($signo) { //TODO something }); }
这些都执行后,会执行Crontab::load_config();
该方法会读取到我们配置文件的读取路径中的数据,在main.php里面params_c方法会默认从ROOT_PATH . "config/crontab.php"中获取
Crontab.class.php:line162->ParseCrontab::parse($task["rule"], $time);//这里是解析定时任务的规则返回值是每个任务的下次执行的间隔(秒级),类似linux自带的crontab的规则,这个放到后面讲解
TickTable::set_task($ret, array_merge($task, array("id" => $id)));//这里会将读取到任务的规则写到ticktable里面
self::register_timer();//这里才是任务调度的关键,每隔1分钟会将到下一分钟需要执行的任务插入到ticktable里面,然后每隔1s钟执行task
下一讲:do_something方法