玩转 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~

本作品采用《CC 协议》,转载必须注明作者和本文链接
UKNOW
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!