CmdSignal.php 3.6 KB
<?php

namespace App\Console\Commands;


use Illuminate\Console\Command;

/**
 * TODO:: 如果想要在终止 任务时不让数据丢失或者异常,请使用此类
 * @author:dc
 * @time 2023/8/21 11:03
 * Class CmdSignal
 * @package App\Console\Commands
 */
trait CmdSignal
{

    /**
     * 是否停止
     * @var bool
     */
    public $isStop = false;

    /**
     * 超时未退出,强制退出 暂时未实现
     * @var int
     */
    public $stopTimeOut = 30;


    public $debugLogFile = null;

    /**
     * 调试输出
     * @param $msg
     * @author:dc
     * @time 2023/8/21 11:22
     */
    public function debug_echo($msg){
        if($this->debugLogFile){
            @file_put_contents($this->debugLogFile,date('Y-m-d H:i:s')." ===> ".print_r($msg,1).PHP_EOL,FILE_APPEND);
        }else{
            echo date('Y-m-d H:i:s')." ===> ".print_r($msg,1).PHP_EOL;
        }
    }

    /**
     * @return bool
     */
    public function handle()
    {

        if($this->isRunning()){
            $this->debug_echo('脚本已运行,请无重复运行');
            return 1;
        }

        $this->debug_echo('已启动脚本');

        // 启动时
        if(method_exists($this,'init')){
            $this->init();
        }

// 注册信号处理程序
//        SIGHUP:终端控制进程时终止或挂起进程
//SIGINT:中断进程(通常由CTRL+C发出)
//SIGQUIT:退出进程并生成核心转储
//SIGILL:非法指令
//SIGABRT:由调试程序触发的异常终止信号
//SIGFPE:浮点异常
//SIGKILL:无条件终止进程
//SIGSEGV:无效的内存引用
//SIGPIPE:写入已关闭的FIFO或套接字时产生的信号
//SIGTERM:要求终止进程的信号
//SIGUSR1:用户定义的信号1
//SIGUSR2:用户定义的信号2
        $handler = function ($signal){
            // 可以处理其他程序
            $this->isStop = true;
        };
        pcntl_signal(SIGTERM, $handler);
        pcntl_signal(SIGINT, $handler);
//        pcntl_signal(SIGHUP, $handler);

        // 检查是否接收到信号
        pcntl_signal_dispatch();

        $tryNum = 0;
// 无限循环,模拟进程运行
        while (true) {
            // 做一些工作... 异常超过5次就重启下进程
            if($this->isStop || $tryNum>5){
                break;
            }

            try {
                $this->start();
            }catch (\Throwable $e){
                $tryNum++;
                // 保证此程序正常
                $this->debug_echo('异常消息:'.$e->getMessage());
                $this->debug_echo('异常文件:'.$e->getFile().':'.$e->getLine());
                $this->debug_echo($e->getTraceAsString());
            }


        }

        $this->debug_echo('已退出程序');


        return Command::SUCCESS;
    }


    /**
     * 获取进程启动名称
     * @return mixed
     * @throws \Exception
     * @author:dc
     * @time 2023/8/21 11:43
     */
    public function getSignature(){
        if(empty($this->signature)){
            throw new \Exception('无法获取到启动命令');
        }
        return $this->signature;
    }

    /**
     * 是否已运行
     * @param int $max 最大运行多少进程
     * @return bool
     * @throws \Exception
     * @author:dc
     * @time 2023/8/21 11:54
     */
    public function isRunning($max=1):bool {

        $ps = "ps -ef | grep \"artisan ".$this->getSignature()."\" | grep -v grep | wc -l";

        $num = exec($ps);

        if(property_exists($this,'maxRunNumber')){
            $max = $this->maxRunNumber;
        }

        if($num>$max){
            return true;
        }

        return false;
    }



}