Codeigniter框架 添加守护进程

守护进程(daemon),就是一个在后台一直运行的进程任务,我们可以用它来处理一些异步任务,比如邮件队列,短信队列,数据接入等等。在laravel里面我们可以直接交给job去处理,然后一个php artisan queue:work 就完事了,非常的方便。但是CI框架并没有这么的优化,因为CI是轻量级的框架,我理解的轻量级就是封装的方法比较少,框架为你做的比较少,自然就轻量级啊,哈哈哈。
1.首先我们定义该进程的执行命令 以及路径

nohup php /home/vagrant/code/project1/web/api/index.php  cli deamon email_queue startup

换做 php 方法就是
$command = "nohup /usr/bin/php  /home/vagrant/code/project1/web/api/index.php   cli deamon email_queue startup" >>  /home/vagrant/code/project1/application/logs/task_warning.log 2>&1 &";

$fp = popen($command, "r");
pclose($fp);

2.我们可以在controller目录下 增加一个文件夹如cli,来存放守护进程文件。

<?php

/**
 * 邮件发送队列
 *
 * Class Email_queue
 * User: UKNOW
 * Date: 2020/6/3 15:55
 */
 class Email_queue extends BASE_Controller
{
  /**
 * 进程停止标志redis key
 * @var string
  */
  private $stop_signal;

  /**
 * 进程最后执行时间 redis key
 * @var string
  */
  private $last_time;

  /**
 * 邮件缓存
  * @var
 */ 
 private $email_cache;

 public function __construct()
 { 
	  parent::__construct();
	  //加载邮件处理类 此类中封装邮件发送的方法
	  $this->load->library('tools/BASE_Email', null, 'email_service');
	  //获取该进程停止redis key,方便管理后台 停止该进程任务
	  $this->stop_signal = $this->_get_redis_key('string:stop_signal');
	  //获取该进程最后一次执行的时间,方便监控这个进程是否运行异常
	  $this->last_time = $this->_get_redis_key('string:last_time');
	  //邮件缓存 防止短时间内发送重复的邮件
	  $this->email_cache = REDIS_PREFIX . 'string:cache:[MD5_CONTENT]';
 }
  /**
 * 组装统一规范的相关redis键
  *
 * @author : UKNOW
 */ 
 private function _get_redis_key($type)
 {  
     // $argv是一个超级全局变量 该变量可以获取进程的 入口文件以及方法
	  global $argv;
	  $cli_params = $argv;
	  array_shift($cli_params);
	  return REDIS_PREFIX . $type . ':' .   implode('_', $cli_params);
 }
  /**
 * 邮件发送执行
  */
  public function startup()
 {  
     //如果是不是CLI模式则 停止执行
	 if (is_cli() == FALSE) exit;
	 //一个死循环
	  while (true) {
	  //判断任务是否开启,如果任务在管理后台关闭 则退出
	  $on_off = $this->redis->get($this->stop_signal);
	  if ($on_off == 'off')  exit;
   
	 //记录最后执行时间,用于监控任务
	  $this->redis->set($this->last_time, time());
	  //获取待处理队列的数据
	  $email = $this->redis->rPop(REDIS_PREFIX . 'list:email');
	  if ($email) {
	  //拿到队列里面邮件 接收人 内容 抄送人等等数据信息
	  $email_arr = json_decode($email, true);
	  $email_title = $email_arr['title'];
	  $email_content = $email_arr['content'];
	  $email_tpl = $email_arr['tpl'];
	  $email_send_to = $email_arr['send_to'];
	  $email_send_cc = $email_arr['send_cc'];
	  $subject = $email_arr['subject'];
      //如果发送人为空,则记录到日志里面,该日志也是一个异步队列,存到数据库,方便查阅
	  if (!$email_send_to) {
		  $log_data = [
		  'log_hash' => md5('INFO' . json_encode($email_arr)),
		  'level' => 'INFO',
		  'severity' => 'info',
		  'message' => '邮件缺少必要参数' . '标题:' . $email_title . ',内容:' . $email_content,
		  'filepath' => '',
		  'line' => 0,
		 ]; 
         $this->redis->lpush(REDIS_PREFIX . "list:error_log", json_encode($log_data));
  continue;
     } 
	 //邮件是否重复判断,如果重复则不再继续执行 
	 $params = [
	  '[MD5_CONTENT]' => md5($email)
	 ]; 
	 $key = strtr($this->email_cache, $params);
	  if ($this->redis->get($key)) continue;
     $this->redis->setex($key, 20 * 60, $email_content);

  $view_param =[
  'email_tpl' => $email_tpl,
  'email_title' => $email_title,
  'email_content' => $email_content
  ];
  //整理邮件模版内容
  $body = $this->load->view('email/templete', $view_param, true);
  //调取邮件类的发送方法
  $result = $this->email_service->send($email_send_to, $email_title, $subject,   $body, null, $email_send_cc);
  //如果发送失败 则记录日志
  if ($result === FALSE) {
	  $log_data = [
	  'log_hash' => md5('INFO' . json_encode($email_arr)),
	  'level' => 'INFO',
	  'severity' => 'info',
	  'message' => '邮件发送失败' . '标题:' . $email_title . $result,
	  'filepath' => '',
	  'line' => 0,
	 ];  
     $this->redis->lpush(REDIS_PREFIX . "list:error_log", json_encode($log_data));
   }
 } else {
      // 如果没有从队列中获取数据,则3s睡眠,减少资源消耗,同时再次链接redis,防止链接丢失
	  sleep(3);
	  $this->redis->reconnect();
       }
    }
 }
 }

3.管理后台,查看执行中的进程,获取进程最后执行时间,关闭和开启进程

  //获取 正在运行的进程
  public function get_running_daemon()
{
	  $daemon_str = '';
	  /**
	  * 这个相当于 在linux命令航 执行 ps.....命令
	  * grep 'php' 筛选php相关进程,grep 'project1' 筛选本项目的相关进程,grep -v 去除,awk '{print $2...}' 是获取我们所想要的列的数据,大家可以自己执行下 ps -aux 结果看一下需要哪些列的数据
	  */
	  $handler = popen("ps -aux | grep 'php' | grep 'project1' | grep -v grep | awk '{print $2,$3,$4,$5,$6,$12,$13,$14,$15,$16}'", "r");
	  
	  while (!feof($handler)) {
	       $daemon_str .= fread($handler, '1024');
	 }
	  pclose($handler);
	  $daemon_arr = explode(PHP_EOL, $daemon_str);
	  
	  $run_daemon_arr = array_map(function ($daemon) {
	           return explode(' ', $daemon);
	 }, array_filter($daemon_arr));
      
	  return $run_daemon_arr;
}
  
 //获取每个进程 最后  执行时间
 public function get_daemon_last_start_time()
{
	  $start_str = '';
	  $handler = popen("ps -eo pid,lstart | grep -v grep | grep -v PID", "r");
	 
	 while (!feof($handler)) {
	          $start_str .= fread($handler, '1024');
	 }  pclose($handler);

	  $start_arr = explode(PHP_EOL, $start_str);
	  $pid_start_arr = [];
	  foreach ($start_arr as $pkey => $pval) {
		  if (empty($pval)) {
			   continue;
		   }
		  $item_pid_time_arr = explode(' ', trim($pval));
		  if (is_array($item_pid_time_arr) && count($item_pid_time_arr) > 0) {
		  $pid = current($item_pid_time_arr);
		  if (empty($pid)) {
		  continue;
	   } 
	  array_shift($item_pid_time_arr);
	  $pid_start_arr[$pid] = date('Y-m-d H:i:s', strtotime(implode(' ', $item_pid_time_arr)));
	   } 
	 } 
	 return $pid_start_arr;
}

 /**
 * 根据执行命令获取进程
  * @param $process_tag
  * @return false|string
  */
public function get_process_id($process_tag)
{
	  $fp = popen("ps -ef|grep '{$process_tag}$'|grep -v grep|awk '{print $2}'", "r");
	  $pid = fread($fp, 512);
	  pclose($fp);
	  return $pid;
}
/**
 * 杀死进程
  * @param $process_tag
  * @return bool
  */
public function kill_process_id($process_tag)
{
	  if (! $process_tag) {
	        return false;
	   } 
	 $fp = popen("ps -ef|grep '{$process_tag}$'|grep -v grep|awk '{print $2}'|xargs kill -9", "r");
	  pclose($fp);
	  return true;
}
  

bingo~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值