A-A+
关于php守护进程的配置及实例
其实守护进程看起来很复杂的样子,其实做起来也是很简单的,前文已经写到了,其实就是一个死循环而已,程序代码不难,重点在于php守护进程的配置文件有点麻烦,想要理解起来确实要费一点脑劲,我用的是tp系统,所以首先需要先搞一个配置文件,在根目录下新建一个 webservice 文件夹,入口文件为 index.php,这个就不用说了。
另外还有两个文件,一个是 webservice.php,主要是php守护进程的一些function方法,文件如下:
- #!/usr/bin/env php
- <?php
- /**
- * @file webservice.php
- *
- * 网站服务管理脚本说明
- *
- * 1. 首先需要根据实际环境对脚本进行配置,请参考 config.php 文件的 Config::getServiceConfig() 方法
- * 2. 调用方法: /path/to/php /path/to/webservice/webservice.php [--help|--force-reboot-all|--force-stop-all] [>> /tmp/webservice.log &]
- *
- * @author lgh_2002@163.com
- * @version v3(修正)
- * @date 2014-11-04
- */
- class websiteService
- {
- /**
- * 单例模式
- * @string public
- */
- static public $_instance = NULL;
- /**
- * 参数命令
- * @string public
- */
- public $command = "";
- /**
- * 脚本运行环境:dev|branches|trunk
- * @access protected
- */
- protected $_env = "";
- /**
- * 脚本执行绝对路径
- * @access protected
- */
- protected $_script_path = "";
- /**
- * 服务进程标识符
- * @access protected
- */
- protected $_service_process = "";
- /**
- * 应用名称
- * @access protected
- */
- protected $_app_name = "chengdun";
- /**
- * 是否显示签名
- * @access public
- */
- public $show_signature = true;
- /**
- * 某个服务
- * @access protected
- */
- public $one_service = '';
- /**
- * 本脚本文件名
- * @access protected
- */
- public $php_self = '';
- /**
- * 日志文件名
- * @access protected
- */
- protected $_log_filename = '';
- /**
- * 构造函数
- *
- * @return
- */
- public function __construct($command = "", $show_signature = true)
- {
- $this->command = $command;
- $this->show_signature = $show_signature;
- $this->php_self = substr($_SERVER['PHP_SELF'], strrpos($_SERVER['PHP_SELF'], '/')+1);
- }
- /**
- * 析构函数
- *
- * @return
- */
- public function __destruct()
- {
- $this->command = '';
- }
- /**
- * 脚本运行入口
- *
- * @return null
- */
- public function execute()
- {
- //签名
- if($this->show_signature) self::showSignature();
- //命令使用帮助
- if(emptyempty($this->command) || "--help" === $this->command) $this->showHelp();
- $now_time = self::getNowTime();
- $msg = $now_time . "\033[33m脚本运行开始......\033[33m";
- $this->showMsg($msg);
- //校验执行锁: 对于写操作-脚本本身必须以单例模式运行
- if("--check" != $this->command)
- {
- $lock_exist = $this->isLockExist();
- if($lock_exist)
- {
- $msg = "检测到有【webservice.php】进程在运行, 请核实...拒绝服务...";
- $this->showMsg($msg, false, true);
- }
- }
- //创建必须的日志目录
- try{
- if(!file_exists(LOG_PATH) || !is_dir(LOG_PATH)) self::createMultiDirectory(LOG_PATH);
- }catch(Exception $e){
- $msg ='日志目录' . LOG_PATH . "不可写,请提供可写的日志目录!!!";
- $this->showMsg($msg, false, true);
- }
- $config = $this->washConfig();
- if(emptyempty($config) || !is_array($config))
- {
- $msg = "检测到异常:无效操作,配置文件内容无效或提供的命令参数无效";
- $this->showMsg($msg, false, true);
- }
- foreach($config as $app => $data)
- {
- $now_time = self::getNowTime();
- $msg = $now_time . "\033[33m开始检测项目【" . $app . "】上的服务......\033[33m";
- $this->showMsg($msg);
- $this->_app_name = $app;
- sleep(1);
- foreach($data as $k => $arg)
- {
- $index = $k + 1;
- $now_time = self::getNowTime();
- if(emptyempty($arg['env']))
- {
- $this->_env = "";
- $cn_env = "【默认】环境";
- }
- else
- {
- $this->_env = $arg['env'];
- $cn_env = "【" . $arg['env'] . "】环境";
- }
- $this->_script_path = $arg['path'];
- $this->_service_process = trim($arg['process']);
- $this->_log_filename = trim($arg['log_name']);
- $msg = $now_time . "\033[33m正在检测" . $cn_env . "第(" . $index . ")项服务:【" . $this->_service_process . "】\033[33m";
- $this->showMsg($msg, true);
- if(!is_file($this->_script_path) || !file_exists($this->_script_path))
- {
- $tmp_script_path = emptyempty($this->_script_path) ? '' : "【" . $this->_script_path . "】";
- $msg = $now_time . "\033[33m检测结果: 所需脚本文件" . $tmp_script_path. "不存在\033[33m";
- $this->showMsg($msg, true);
- continue;
- }
- //检测服务进程
- $pids = $this->_checkService();
- //是否有强制行为
- if(in_array($this->command, array('--force-reboot-all', '--force-stop-all')))
- {
- '--force-stop-all' === $this->command && $action = 2;
- '--force-reboot-all' === $this->command && $action = 3;
- }
- else
- {
- $now_time = self::getNowTime();
- if(emptyempty($pids)) {
- $msg = $now_time . "\033[33m检测结果:服务没有启动:【" . $this->_service_process. "】\033[33m";
- } else {
- $pids = "【进程ID:" . join("|", $pids) . "】";
- $msg = $now_time . "\033[33m检测结果: 服务正常【" . $this->_service_process . "】 - {$pids} \033[33m";
- }
- $this->showMsg($msg, true);
- if('--check' === $this->command)
- {
- continue;
- }
- if(false !== strpos($this->command, "--human"))
- {
- echo "\r\n" . SPACE . "确定要继续操作吗? 【y|n】";
- $op = trim($this->confirm());
- //如果拒绝继续操作,则校验下一个服务
- if('y' != $op)
- {
- echo "\r\n";
- continue;
- }
- echo "\r\n" . SPACE . "请选择操作行为:【1/2/3/4】\r\n\r\n";
- $action = trim($this->selectAction()->confirm());
- }
- else
- {
- $action = $this->getAction();
- }
- }
- switch($action)
- {
- case 1:
- //正常启动
- $now_time = self::getNowTime();
- $pid = $this->startService();
- $msg = "启动服务失败:【" . $this->_service_process . "】, 跳过操作......";
- $pid && $msg = "已成功启动服务:【" . $this->_service_process. "】-【" . $pid . "】";
- $this->showMsg($msg, true);
- break;
- case 2:
- //停止
- $this->stopMultiService();
- break;
- case 3:
- //重启
- $this->stopMultiService();
- $msg = "准备重新启动服务:【" . $this->_service_process. "】";
- $pid = $this->showMsg($msg, true)->startService();
- $msg = "重新启动服务失败:【" . $this->_service_process. "】";
- $pid && $msg = "重新启动服务成功:【" . $this->_service_process. "】-【" . $pid . "】";
- $this->showMsg($msg, true);
- break;
- case 4:
- //退出
- $msg = "退出行为,跳过操作......";
- $this->showMsg($msg, true);
- break;
- default:
- //默认
- $msg = "无效操作,跳过操作......";
- $this->showMsg($msg, true);
- break;
- }
- //休眠2秒
- $sleep_time = 2;
- $msg = "系统正在休眠,请等待{$sleep_time}秒......";
- $this->showMsg($msg, true);
- sleep($sleep_time);
- echo "\r\n";
- }
- }
- $now_time = self::getNowTime();
- $msg = $now_time . "\033[33m脚本运行结束, 安全退出......\033[33m";
- $this->showMsg($msg, false, true);
- }
- /**
- * 获取命令行为
- *
- * @return int
- */
- public function getAction()
- {
- if(false !== strpos ($this->command, "start")){
- $action = 1;
- }else if(false !== strpos ($this->command, "stop")){
- $action = 2;
- }else if(false !== strpos ($this->command, "reboot")){
- $action = 3;
- }else{
- $action = 0;
- }
- return $action;
- }
- /**
- * 启动服务
- *
- * @return int
- */
- public function startService()
- {
- $output = $this->_checkService();
- if(emptyempty($output) || !is_array($output))
- {
- $cmd = "nohup " . PHP_BIN . " $this->_script_path $this->_service_process >> " . LOG_PATH . "{$this->_log_filename}.log 2>&1 & ";
- exec($cmd);
- $output = $this->_checkService();
- }
- $pid = emptyempty($output) ? 0 : (int)trim($output[0]);
- return $pid;
- }
- /**
- * 停止服务
- *
- * @return bool
- */
- public function stopService($pid)
- {
- if($pid <= 0) return false;
- $cmd = "kill -9 {$pid} > /dev/null &";
- exec($cmd, $output, $status);
- if(0 === $status) return true;
- return false;
- }
- /**
- * 停止多个服务
- *
- * @return mixed
- */
- public function stopMultiService()
- {
- $pids = $this->_checkService();
- if(emptyempty($pids) || !is_array($pids))
- {
- $msg = "停止服务失败,检测到服务没有启动:【" . $this->_service_process. "】";
- $this->showMsg($msg, true);
- return false;
- }
- foreach($pids as $k => $pid)
- {
- $msg = "当前进程操作无效:【" . $this->_service_process. "】" . "-" . "【" . $pid. "】";
- $rs = $this->stopService($pid);
- if($rs)
- {
- $msg = "当前进程已杀死:【" . $this->_service_process. "】" . "-" . "【" . $pid. "】";
- }
- $this->showMsg($msg, true);
- }
- }
- /**
- * 检测服务进程: 返回进程ID集合
- *
- * @return array
- */
- protected function _checkService()
- {
- //注意!!!:防止进程没有及时退出,这里必须休眠一会儿
- sleep(2);
- $enviroment = emptyempty($this->_env) ? "" : "| grep " . $this->_env;
- $cmd = "ps aux | grep -v " . $this->php_self . " | grep " . $this->_app_name . "| grep " . $this->_service_process . $enviroment . "| grep -v grep | awk '{print $2}'";
- /*
- *$msg = "执行命令如下:" . $cmd;
- *$this->showMsg($msg);
- */
- exec($cmd, $pids);
- return $pids;
- }
- /**
- * 消息弹窗
- *
- * @param int $msg
- * @param int $indent
- * @param int $exit
- *
- * @return mixed
- */
- public function showMsg($msg, $indent = false, $exit = false)
- {
- emptyempty($msg) && $msg = "unknow msg....";
- !is_string($msg) && $msg = json_encode($msg);
- $indent_content = "";
- $indent && $indent_content = SPACE;
- $sign = str_repeat("-", 110);
- echo START_COLOR . $indent_content . $sign . END_COLOR;
- echo START_COLOR . $indent_content . $msg . END_COLOR;
- if($exit)
- {
- echo START_COLOR . $indent_content . $sign . END_COLOR;
- exit;
- }
- return $this;
- }
- /**
- * 消息确认: 流
- *
- * @return string
- */
- public function confirm()
- {
- $fp = fopen('php://stdin', 'r');
- $input = fgets($fp, 255);
- fclose($fp);
- return $input;
- }
- /**
- * 操作行为分支
- *
- * @return object
- */
- public function selectAction()
- {
- $a = "start";
- $b = "stop";
- $c = "reboot";
- $d = "exit";
- echo START_COLOR . SPACE . "==================================================" . END_COLOR;
- echo START_COLOR . SPACE . "(1) $a" . END_COLOR;
- echo START_COLOR . SPACE . "(2) $b" . END_COLOR;
- echo START_COLOR . SPACE . "(3) $c" . END_COLOR;
- echo START_COLOR . SPACE . "(4) $d" . END_COLOR;
- echo START_COLOR . SPACE . "==================================================" . END_COLOR;
- echo "\r\n";
- return $this;
- }
- /**
- * 获取当前系统时间
- *
- * @return string
- */
- public function getNowTime()
- {
- $now_time = date("Y-m-d H:i:s");
- $now_time = "\033[33m【" . $now_time . "】\033[33m";
- return $now_time;
- }
- /**
- * 单进程执行锁
- *
- * @return bool
- */
- public function isLockExist()
- {
- $cmd = "ps aux | grep -i 'webservice.php' | grep -v grep | grep -v vi | grep -v vim | wc -l";
- exec($cmd, $result);
- //运行锁
- if(!emptyempty($result) && $result[0] > 1)
- {
- return true;
- }
- return false;
- }
- /**
- * 创建多级目录
- *
- * @param string $dir 层级目录: a/b/c
- * @param string $mode 权限
- *
- * @return boolean
- */
- static public function createMultiDirectory($dir, $mode = 0777)
- {
- if(is_dir($dir))
- {
- return true;
- }
- if(!self::createMultiDirectory(dirname($dir), $mode))
- {
- return false;
- }
- return mkdir($dir, $mode);
- }
- /**
- * 脚本使用帮助信息
- *
- * @return exit
- */
- public function showHelp()
- {
- $config = Config::getServiceConfig();
- $head = SERVICE_SPACE . "【项目名称】 【服务名称】 ". SERVICE_SPACE . SERVICE_SPACE . SERVICE_SPACE ."【备注说明】\r\n";
- $service_list = SERVICE_SPACE;
- foreach($config as $app => $v)
- {
- foreach($v as $k1 => $v1)
- {
- $v1['remark_space'] <= 0 && $v1['remark_space'] = 1;
- $remark_space = $v1['remark_space'] <= 0 ? 1 : str_repeat(" ", $v1['remark_space']);
- $service_list .= " {$app}" . SERVICE_SPACE . $v1['process'] . SERVICE_SPACE . $remark_space . $v1['remark'] . "\r\n" . SERVICE_SPACE;
- }
- }
- $service_list = $head . $service_list;
- $self_script = $_SERVER['PHP_SELF'];
- $sign = str_repeat("=", 136) . "\r\n";
- echo $sign;
- echo "\r\n";
- echo " 全站服务管理脚本使用说明\r\n";
- echo "\r\n";
- echo " 1. 首先请根据实际环境对 /path/to/webservice/config.php 脚本进行配置, 请参考 Config::getServiceConfig() 方法\r\n";
- echo "\r\n";
- echo " 2. 调用方法:/path/to/php /path/to/" . $self_script . " [--help|--check|--force-reboot-all|--force-stop-all] [>> /tmp/webservice.log &]\r\n";
- echo "\r\n";
- echo " 3. 每一个服务进程都有对应的运行日志记录, 存放路径请参考 LOG_PATH 定义段 \r\n";
- echo "\r\n";
- echo " 4. 参数说明:\r\n";
- echo "\r\n";
- echo " --help 打印脚本使用说明\r\n";
- echo " --check 检测所有配合服务运行情况 \r\n";
- echo " --check-server-name 检测 server-name 服务运行情况 \r\n";
- echo " --human 启用交互模式操作服务\r\n";
- echo " --human-server-name 启用交互模式操作 server-name 服务\r\n";
- echo " --start-server-name 启动 server-name 服务\r\n";
- echo " --stop-server-name 停止 server-name 服务\r\n";
- echo " --reboot-server-name 启动 server-name 服务\r\n";
- echo " --force-reboot-all 强制重启所有服务 \r\n";
- echo " --force-stop-all 强制停止所有服务 \r\n";
- echo "\r\n";
- echo " 5. 可用的服务列表: \r\n";
- echo "\r\n";
- echo $service_list;
- echo "\r\n";
- echo $sign;
- echo "\r\n";
- exit;
- }
- /**
- * 签名
- *
- * @return string
- */
- static public function showSignature()
- {
- $timer = 0;
- $data = '';
- $total = 62;
- while($timer <= $total)
- {
- $data = $timer == $total/2 ? "全站服务管理" : "▆";
- echo $data;
- usleep(10000);
- $timer++;
- }
- echo "\r\n";
- }
- /**
- * 清洗配置文件
- *
- * @return
- */
- public function washConfig()
- {
- $config = array();
- //读取配置文件
- $_config = Config::getServiceConfig();
- if(in_array($this->command, array('--force-reboot-all', '--force-stop-all', '--check', '--human')))
- {
- return $_config;
- }
- //重新清洗配置文件
- $pos = strpos($this->command, "-", 2);
- $command = substr($this->command, $pos+1);
- foreach($_config as $k => $v)
- {
- foreach($v as $k1 => $v1)
- {
- if($command == $v1['process'])
- {
- $config[$k][$k1] = $v1;
- }
- }
- }
- return $config;
- }
- /**
- * 获取单例
- *
- * @return object
- */
- static public function getInstance($command = '', $show_signature = true)
- {
- if(!self::$_instance instanceof self)
- {
- self::$_instance = new self($command, $show_signature);
- }
- return self::$_instance;
- }
- }
- //www.xiariboke.net
- //加载配置文件
- if(!file_exists(dirname(__FILE__) . "/config.php")) exit("配置文件加载失败......, 不存在?请检查下!!!\n\n");
- require(dirname(__FILE__) . "/config.php");
- //开始执行脚本
- $command = isset($argv[1]) ? $argv[1] : "";
- websiteService::getInstance($command, SHOW_SIGNATURE)->execute();
这个是很关键的一个文件,如果要启动某个php守护进程的话,是需要调用这里面的方法的,看着这个方法有些复杂,其实执行下来,是很直观的,我们再来看一下config.php配置文件,主要是用来配置进程的,在这个里面来启动进程,配置文件如下:
- <?php
- /**
- * @file config.php
- *
- * @author lgh_2002@163.com
- * @version v3(修正)
- * @date 2014-11-04
- */
- PHP_SAPI == 'cli' or die('拒绝服务!!');
- set_time_limit(0);
- ignore_user_abort(true);
- date_default_timezone_set('Asia/Shanghai');
- define("WEBSERVICE_ROOT_PATH", dirname(__FILE__));
- define("START_COLOR", "\033[32m");
- define("END_COLOR", "\033[0m\r\n");
- define("SPACE", " ");
- define("SERVICE_SPACE", " ");
- define("PHP_BIN", "/application/php/bin/php ");
- !defined("SHOW_SIGNATURE") && define("SHOW_SIGNATURE", false);
- !defined("LOG_PATH") && define("LOG_PATH", "/tmp/websiteService/");
- class Config
- {
- /**
- * 获取配置信息
- *
- * @return array
- */
- static public function getServiceConfig()
- {
- //注意:脚本路径一定要配置正确
- $path = array(
- "p1" => "/application/nginx/html/www/chengdun/webservice/index.php",
- );
- $config = array(
- /*'chengdun' => array(
- array(
- "env" => "",
- "path" => $path['p1'],
- "process" => "Home-Index-demo",
- "remark" => "测试",
- "remark_space" => 6,
- 'log_name' => 'demo',
- ),
- array(
- "env" => "branches",
- "path" => $path['p1'],
- "process" => "default-daemon-secondaryrepay",
- ),
- array(
- "env" => "branches",
- "path" => $path['p1'],
- "process" => "default-daemonfastrepay-run",
- ),
- array(
- "env" => "branches",
- "path" => $path['p1'],
- "process" => "default-daemon-remind",
- ),
- array(
- "env" => "branches",
- "path" => $path['p1'],
- "process" => "default-daemon-luckchance",
- ),
- array(
- "env" => "dev",
- "path" => $path['p3'],
- "process" => "admin-daemon-sms",
- ),
- array(
- "env" => "dev",
- "path" => $path['p3'],
- "process" => "admin-daemon-email",
- ),
- ),*/
- /*'baibaodai' => array(
- array(
- "env" => "dev",
- "path" => $path['p2'],
- "process" => "default-daemon-secondaryrepay",
- ),
- array(
- "env" => "dev",
- "path" => $path['p2'],
- "process" => "default-daemonfastrepay-run",
- ),
- array(
- "env" => "dev",
- "path" => $path['p2'],
- "process" => "default-daemon-remind",
- ),
- ),*/
- 'Credit' => array(
- array(
- "env" => "",
- "path" => $path['p1'],
- "process" => "Credit-Daemon-insuredata",
- "remark" => "同步保函",
- "remark_space" => 13,
- 'log_name' => 'insuredata_log',
- ),
- array(
- 'env' => "",
- 'path' => $path['p1'],
- "process" => "Credit-Daemon-giftCoupon",
- "remark" => "同步礼券",
- "remark_space" => 13,
- 'log_name' => 'giftCoupon_log',
- ),
- array(
- 'env' => "",
- 'path' => $path['p1'],
- "process" => "Credit-Daemon-company",
- "remark" => "同步公司信息",
- "remark_space" => 16,
- 'log_name' => 'company_log',
- ),
- ),
- 'Angent' => array(
- array(
- "env" => "",
- "path" => $path['p1'],
- "process" => "Angent-Daemon-spend_count",
- "remark" => "更新消费总额",
- "remark_space" => 12,
- 'log_name' => 'spend_count_log',
- ),
- array(
- "env" => "",
- "path" => $path['p1'],
- "process" => "Angent-Daemon-give_experience",
- "remark" => "处理用户经验",
- "remark_space" => 12,
- 'log_name' => 'give_experience_log',
- ),
- array(
- "env" => "",
- "path" => $path['p1'],
- "process" => "Angent-Daemon-return_insure_subsidy",
- "remark" => "",
- "remark_space" => 12,
- 'log_name' => 'return_insure_subsidy',
- ),
- ),
- );
- //www.xiariboke.net
- return $config;
- }
- }
好了,到此为止,php守护进程配置文件以及方法文件就怎么多了,好了之后,我们就可以在这个基础上去写控制器文件了,其实就是public function一个死循环文件而已,看下面一个具体实例:
- /**
- * 处理保函补贴
- */
- public function return_insure_subsidy(){
- while(true){
- $row = \Helper_Redis::instance()->pop('list_mapi_insure_subsidy');
- if($row){
- $res = D('Angent/InsureData')->return_insure_subsidy($row['company_id'], $row['product_id'], $row['money'], $row['id']);
- pprint($row);
- }//www.xiariboke.net
- usleep(100000);
- }
- }
我们只要打开这个守护进程,这个程序便会一直循环下去,只要不stop,便不会停止,只要有订单数据,就会马上同步,像这种守护进程,更多的适合用在执行同步发送邮件,或者短信,以及其它一些及时同步的应用上面。