IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> Swoole+ThinkPHP6自定义命令实现Mysql自动备份定时任务 -> 正文阅读

[PHP知识库]Swoole+ThinkPHP6自定义命令实现Mysql自动备份定时任务

尽管crontab+shell已经很强大很好用了,但在部署时,还是需要专门去写crontab配置,有那么一丢丢不方便,这里将备份配置放到项目里来,可以实现统一管理。

脚本直接采用swoole定时器实现,并通过swoole的蜕变守护进程达到常驻内存运行目的。之所以不在swoole的server里,通过workstart调用定时器,是因为server还需要监听端口,不够简单直接,而且在服务退出时,work进程是没法响应信号退出的,只能被master强制回收。

下面开始具体实现

一、创建thinkphp自定义命令的类文件

php think make:command TaskMysqlBackup task:mysqlbackup

CLI模式下,执行以上命令后会自动创建app/command/TaskMysqlBackup.php文件,也可以不用命令手动创建。

二、编辑TaskMysqlBackup.php类文件,实现自定义命令:

<?php
declare (strict_types = 1);

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;

class TaskMysqlBackup extends Command
{
    protected $pid_file;
    protected $log_file;
    protected $after_timer;
    protected $tick_timer;
    protected $period;
    protected $worktime;
    protected $bin_dir;
    protected $bak_dir;
    protected $keep;

    protected function configure()
    {
        // 指令配置
        $this->setName('task:mysqlbackup')
            ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|status|backup", 'start')
            ->setDescription('数据库定时备份');

        $this->log_file = root_path() . 'extend' . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'mysqlbackup.log';
        // pid文件不要放到runtime目录,否则容易被清除掉,造成进程无法正常关闭
        $this->pid_file = root_path() . 'extend' . DIRECTORY_SEPARATOR . 'marks' . DIRECTORY_SEPARATOR . 'mysqlbackup.pid';

        // 运行周期(秒)
        $this->period = 86400;
        // 运行时间点,进程生命周期内只有一个值,不一定就是上一次/下一次的运行时间点,运行周期在时间轴上分割出一系列的点,这些点平移后,其中的某一个点与运行时间点重合
        $this->worktime = strtotime(date('Y-m-d 03:00:00'));
        // mysqldump命令所在目录
        $this->bin_dir = '/usr/bin/';
        // 备份文件存放目录
        $this->bak_dir = root_path() . 'extend' . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR;
        // 备份文件保留时间(秒)
        $this->keep = 7 * 86400;
    }

    protected function execute(Input $input, Output $output)
    {
        if (!extension_loaded('swoole')) {
            $output->error('Can\'t detect Swoole extension installed.');
            return;
        }
        $action = $input->getArgument('action');
        if (in_array($action, ['start', 'stop', 'restart', 'status', 'backup'])) {
            $this->$action();
        } else {
            $output->error("Invalid argument action:{$action}, Expected start|stop|restart|status|backup.");
            return;
        }
    }

    protected function checkFile($file)
    {
        $dir = dirname($file);
        if (!is_dir($dir)) {
            @mkdir($dir, 0700, true);
        }
        @touch($file);
        if (!is_writable($file)) {
            return false;
        }
        return true;
    }

    protected function backup()
    {
        $database = config('database.connections.mysql.database');
        $username = config('database.connections.mysql.username');
        $password = config('database.connections.mysql.password');

        $host = config('database.connections.mysql.hostname');
        $port = config('database.connections.mysql.hostport');
        $charset = config('database.connections.mysql.charset');

        $time = date('Ymd_His');

        $filename = "{$this->bak_dir}{$database}_{$time}.gz";
        if (!$this->checkFile($filename)) {
            echo "[" . date('Y-m-d H:i:s') . "]\r\nCan\'t create file under: {$this->bak_dir}.\r\n\r\n";
            return;
        }
        @unlink($filename);

        $this->clear($database);

        $command = "{$this->bin_dir}mysqldump -h{$host} -P{$port} --default-character-set={$charset} -u{$username} -p{$password} {$database}";
        $command .= "|gzip>{$filename}";

        $result = shell_exec($command);
        if (!empty($result)) {
            echo "[" . date('Y-m-d H:i:s') . "]\r\nBackup failure.\r\n\r\n";
            return;
        }
    }

    protected function clear($filePrefix)
    {
        foreach (glob($this->bak_dir . '*.gz') as $file) {
            if (preg_match('/^' . $filePrefix . '_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})\.gz$/', basename($file), $matches)) {
                $time = mktime((int) $matches[4], (int) $matches[5], (int) $matches[6], (int) $matches[2], (int) $matches[3], (int) $matches[1]);
                if ($time !== false && $time <= time() - $this->keep) {
                    @unlink($file);
                }
            }
        }
    }

    protected function start()
    {
        if ($this->isRunning()) {
            $this->output->error('Process is already running.');
            return;
        }
        $this->output->writeln('Starting process...');

        if (!$this->checkFile($this->pid_file)) {
            $this->output->error("Can\'t write file: {$this->pid_file}.");
            return;
        }
        if (!$this->checkFile($this->log_file)) {
            $this->output->error("Can\'t write file: {$this->log_file}.");
            return;
        }

        $timespan = ($this->worktime - time()) % $this->period;
        if ($timespan <= 0) {
            $timespan += $this->period;
        }

        $this->after_timer = \swoole_timer::after($timespan * 1000, function () {
            $this->tick_timer = \swoole_timer::tick($this->period * 1000, function () {
                ob_start();
                $this->backup();
                file_put_contents($this->log_file, ob_get_clean(), FILE_APPEND);
            });
            ob_start();
            $this->backup();
            file_put_contents($this->log_file, ob_get_clean(), FILE_APPEND);
        });

        \swoole_process::daemon(true, false);

        file_put_contents($this->pid_file, getmypid());

        \swoole_process::signal(SIGTERM, function () {
            if ($this->after_timer) {
                \swoole_timer::clear($this->after_timer);
            }
            if ($this->tick_timer) {
                \swoole_timer::clear($this->tick_timer);
            }
            @unlink($this->pid_file);
        });

        $nextTime = date('Y-m-d H:i:s', time() + $timespan);
        $this->output->writeln("Starting success, Task will run in the {$nextTime} for the first time.");

        \swoole_event::wait();
    }

    protected function stop()
    {
        if (!$this->isRunning()) {
            $this->output->error('No process running.');
            return;
        }
        $this->output->writeln('Stopping process...');
        $pid = (int) file_get_contents($this->pid_file);
        \swoole_process::kill($pid, SIGTERM);
        $end = time() + 15;
        while (time() < $end && \swoole_process::kill($pid, 0)) {
            usleep(100000);
        }
        if ($this->isRunning()) {
            $this->output->error('Unable to stop the process.');
            return;
        }
        $this->output->writeln('Stopping success.');
    }

    protected function restart()
    {
        if ($this->isRunning()) {
            $this->stop();
        }
        $this->start();
    }

    protected function status()
    {
        $this->output->writeln($this->isRunning() ? 'Process is running.' : 'Process stopped.');
    }

    protected function isRunning()
    {
        if (!is_readable($this->pid_file)) {
            return false;
        }
        $pid = (int) file_get_contents($this->pid_file);
        return $pid > 0 && \swoole_process::kill($pid, 0);
    }
}

三、配置命令使生效

config/console.php修改配置如下:

<?php
return [
    'commands' => [
        'task:mysqlbackup' => 'app\command\TaskMysqlBackup',
    ],
];

四、手动运行命令测试备份逻辑

php think task:mysqlbackup backup

五、开启自动备份

php think task:mysqlbackup start

此时,进程已常驻后台,会定时进行备份

虽然前面有提到蜕变守护进程,但并不是多了一个守护进程,只是swoole变为后台进程的说法,要想真正守护此进程,得另外实现一个进程来监控此进程,当然更简单的方法是采用第三方守护工具,比如Python的supervisor。

命令帮助:

php think task:mysqlbackup -h

Usage:
  task:mysqlbackup [<action>]

Arguments:
  action                start|stop|restart|status|backup [default: "start"]

Options:
  -h, --help            Display this help message
  -V, --version         Display this console version
  -q, --quiet           Do not output any message
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-07-10 14:19:19  更:2021-07-10 14:21:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 1:17:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码