/** * * 添加一个任务 * * @param int $time_long 多长时间运行一次 单位秒 * @param callback $func 任务运行的函数或方法 * @param mix $args 任务运行的函数或方法使用的参数 * @return void */ public static function add($time_long, $func, $args = array(), $persistent = true) { if ($time_long <= 0) { return false; } if (!is_callable($func) && class_exists('Log')) { \Man\Core\Lib\Log::add(var_export($func, true) . "not callable\n"); return false; } $time_now = time(); $run_time = $time_now + $time_long; if (!isset(self::$tasks[$run_time])) { self::$tasks[$run_time] = array(); } self::$tasks[$run_time][] = array($func, $args, $persistent, $time_long); return true; }
/** * 处理业务 * @see Worker::dealProcess() */ public function dealProcess($buffer) { $buffer = trim($buffer); $ip = $this->getRemoteIp(); if ($ip != '127.0.0.1' && $buffer == 'status') { \Man\Core\Lib\Log::add("IP:{$ip} {$buffer}"); } // 判断是否认证过 $this->adminAuth[$this->currentDealFd] = !isset($this->adminAuth[$this->currentDealFd]) ? 0 : $this->adminAuth[$this->currentDealFd]; if ($this->adminAuth[$this->currentDealFd] < 3) { if ($buffer != \Man\Core\Lib\Config::get($this->workerName . '.password')) { if (++$this->adminAuth[$this->currentDealFd] >= 3) { $this->sendToClient("Password Incorrect \n"); $this->closeClient($this->currentDealFd); return; } $this->sendToClient("Please Try Again\n"); return; } else { $this->adminAuth[$this->currentDealFd] = time(); $this->sendToClient("Hello Admin \n"); return; } } // 单独停止某个worker进程 if (preg_match("/kill (\\d+)/", $buffer, $match)) { $pid = $match[1]; $this->sendToClient("Kill Pid {$pid}\n"); if (!posix_kill($pid, SIGHUP)) { $this->sendToClient("Pid Not Exsits\n"); } return; } $master_pid = file_get_contents(WORKERMAN_PID_FILE); switch ($buffer) { // 展示统计信息 case 'status': $status = $this->getMasterStatus(); if (empty($status)) { $this->sendToClient("Can not get Master status, Extension sysvshm or sysvmsg may not enabled\n"); return; } $worker_pids = $this->getWorkerPidMap(); $pid_worker_name_map = $this->getPidWorkerMap(); foreach ($worker_pids as $worker_name => $pid_array) { if ($this->maxWorkerNameLength < strlen($worker_name)) { $this->maxWorkerNameLength = strlen($worker_name); } } foreach (\Man\Core\Lib\Config::getAllWorkers() as $worker_name => $config) { if (!isset($config['listen'])) { continue; } if ($this->maxAddressLength < strlen($config['listen'])) { $this->maxAddressLength = strlen($config['listen']); } } $msg_type = $message = 0; // 将过期的消息读出来,清理掉 if (\Man\Core\Master::getQueueId()) { while (@msg_receive(\Man\Core\Master::getQueueId(), self::MSG_TYPE_STATUS, $msg_type, 1000, $message, true, MSG_IPC_NOWAIT)) { } } $loadavg = sys_getloadavg(); $this->sendToClient("---------------------------------------GLOBAL STATUS--------------------------------------------\n"); $this->sendToClient(\Man\Core\Master::NAME . ' version:' . \Man\Core\Master::VERSION . "\n"); $this->sendToClient('start time:' . date('Y-m-d H:i:s', $status['start_time']) . ' run ' . floor((time() - $status['start_time']) / (24 * 60 * 60)) . ' days ' . floor((time() - $status['start_time']) % (24 * 60 * 60) / (60 * 60)) . " hours \n"); $this->sendToClient('load average: ' . implode(", ", $loadavg) . "\n"); $this->sendToClient(count($this->connections) . ' users ' . count($worker_pids) . ' workers ' . count($pid_worker_name_map) . " processes\n"); $this->sendToClient(str_pad('worker_name', $this->maxWorkerNameLength) . " exit_status exit_count\n"); foreach ($worker_pids as $worker_name => $pid_array) { if (isset($status['worker_exit_code'][$worker_name])) { foreach ($status['worker_exit_code'][$worker_name] as $exit_status => $exit_count) { $this->sendToClient(str_pad($worker_name, $this->maxWorkerNameLength) . " " . str_pad($exit_status, 16) . " {$exit_count}\n"); } } else { $this->sendToClient(str_pad($worker_name, $this->maxWorkerNameLength) . " " . str_pad(0, 16) . " 0\n"); } } $this->sendToClient("---------------------------------------PROCESS STATUS-------------------------------------------\n"); $this->sendToClient("pid\tmemory " . str_pad(' listening', $this->maxAddressLength) . " timestamp " . str_pad('worker_name', $this->maxWorkerNameLength) . " " . str_pad('total_request', 13) . " " . str_pad('packet_err', 10) . " " . str_pad('thunder_herd', 12) . " " . str_pad('client_close', 12) . " " . str_pad('send_fail', 9) . " " . str_pad('throw_exception', 15) . " suc/total\n"); if (!\Man\Core\Master::getQueueId()) { return; } $time_start = time(); unset($pid_worker_name_map[posix_getpid()]); $total_worker_count = count($pid_worker_name_map); foreach ($pid_worker_name_map as $pid => $worker_name) { posix_kill($pid, SIGUSR1); if ($this->getStatusFromQueue()) { $total_worker_count--; } } while ($total_worker_count > 0) { if ($this->getStatusFromQueue()) { $total_worker_count--; } if (time() - $time_start > 1) { break; } } break; // 停止server // 停止server case 'stop': if ($master_pid) { $this->sendToClient("stoping....\n"); posix_kill($master_pid, SIGINT); } else { $this->sendToClient("Can not get master pid\n"); } break; // 平滑重启server // 平滑重启server case 'reload': $pid_worker_name_map = $this->getPidWorkerMap(); unset($pid_worker_name_map[posix_getpid()]); if ($pid_worker_name_map) { foreach ($pid_worker_name_map as $pid => $item) { posix_kill($pid, SIGHUP); } $this->sendToClient("Restart Workers\n"); } else { if ($master_pid) { posix_kill($master_pid, SIGHUP); $this->sendToClient("Restart Workers\n"); } else { $this->sendToClient("Can not get master pid\n"); } } break; // admin管理员退出 // admin管理员退出 case 'quit': $this->sendToClient("Admin Quit\n"); $this->closeClient($this->currentDealFd); break; case '': break; default: $this->sendToClient("Unkonw CMD \nAvailable CMD:\n status show server status\n stop stop server\n reload graceful restart server\n quit quit and close connection\n kill pid kill the worker process of the pid\n"); } }
/** * 检查文件更新时间,如果有更改则平滑重启服务(开发的时候用到) * @return void */ public function checkFilesModify() { $has_send_signal = false; foreach ($this->filesToInotify as $file => $mtime) { clearstatcache(); $stat = @stat($file); if (false === $stat) { unset($this->filesToInotify[$file]); continue; } $mtime_now = $stat['mtime']; if ($mtime != $mtime_now) { $this->filesToInotify[$file] = $mtime_now; if (!$has_send_signal) { \Man\Core\Lib\Log::add("{$file} updated and reload workers"); $this->sendSignalToAllWorker(SIGHUP); $has_send_signal = true; } } } }
/** * 记录日志 * @param sring $str * @return void */ protected function notice($str, $display = true) { $str = 'Worker[' . get_class($this) . ':' . posix_getpid() . ']:' . $str; Log::add($str); if ($display && Config::get('workerman.debug') == 1) { echo $str . "\n"; } }
/** * notice,记录到日志 * @param string $msg * @param bool $display * @return void */ public static function notice($msg, $display = false) { Log::add("Server:" . trim($msg)); if ($display) { if (self::$serviceStatus == self::STATUS_STARTING && @posix_ttyname(STDOUT)) { echo $msg . "\n"; } } }