GithubHelp home page GithubHelp logo

white-poto / php_crontab Goto Github PK

View Code? Open in Web Editor NEW
305.0 32.0 97.0 239 KB

A crontab written in PHP based on pcntl and react/event-loop

License: Apache License 2.0

PHP 88.48% Shell 11.52%
php-crontab crontab-task crontab-service crontab-manager crontab-format

php_crontab's Introduction

php_crontab

Latest Stable Version Total Downloads Latest Unstable Version License License

php crontab base on pcntl and react/event-loop

中文说明

Why use php_crontab?

When we have a handful of crontab tasks, crontab service is enough for us to manage them. If we have many crontab tasks, there will be some problems like:

  • The crontab tasks are managed in a text file. If there are no comment, it will be hard for fresh man to understand what they are.
  • If the crontab tasks are distributed in different servers, it will be hard to manage them.
  • If you want to collect the crontab tasks' logs, it will be not easy.
  • Tasks of different users must written in different files. Based on the above reasons, we need a crontab manager which can manage crontab tasks together and configure the tasks.

How to use php_crontab?

First composer require jenner/crontab.
There are two ways to use php_crontab to manage your crontab tasks. You can just write a php script and add it to the crontab config file with the command crontab -e. The php script should run every minute. For example tests/simple.php
Or you can write a php daemon script which will run as a service and will not exit until someone kill it. It will check the tasks every minute. For example tests/daemon.php

Import

composer require jenner/crontab

Properties

  • The crontab tasks can be stored in any way you what. For example, mysql, reids. What's more? You can develop a web application to manage them.
  • The tasks of different users can be managed together.
  • Multi-Process, every task is a process.
  • You can set the user and group of a crontab task
  • STDOUT can be redirected
  • Based on react/event-loop, it can run as a daemon.
  • A HTTP server which you can manage the crontab tasks through it.
  • Dynamic task loader, you can register a task loader by Daemon::registerTaskLoader, which will execute every 60 seconds and update the crontab tasks.

HTTP interfaces

HTTP METHOD: GET

  • add add new task to crontab server
  • get_by_name get task by name
  • remove_by_name remove task by name
  • clear clear all task
  • get get all tasks
  • start start crontab loop
  • stop stop crontab loop

Examples:

http://host:port/add?name=name&cmd=cmd&time=time&out=out&user=user&group=group&comment=comment
http://host:port/get_by_name?name=name
http://host:port/remove_by_name?name=name
http://host:port/clear
http://host:port/get
http://host:port/start
http://host:port/stop

TODO

  • add log handler interface.
  • add http log handler, socket log handler, file handler and so on.
  • separate stdout and stderr. use different handlers

run based on crontab service

* * * * * php demo.php
<?php
$missions = [
    [
        'name' => 'ls',
        'cmd' => "ls -al",
        'out' => '/tmp/php_crontab.log',
        'err' => '/tmp/php_crontab.log',
        'time' => '* * * * *',
        'user' => 'www',
        'group' => 'www'
    ],
    [
        'name' => 'ls',
        'cmd' => "ls -al",
        'out' => '/tmp/php_crontab.log',
        'err' => '/tmp/php_crontab.log',
        'time' => '* * * * *',
        'user' => 'www',
        'group' => 'www'
    ],
];

$tasks = array();
foreach($missions as $mission){
    $tasks[] = new \Jenner\Crontab\Mission($mission['name'], $mission['cmd'], $mission['time'], $mission['out']);
}

$crontab_server = new \Jenner\Crontab\Crontab(null, $tasks);
$crontab_server->start(time());

run as a daemon

it will check the task configs every minute.

$missions = [
    [
        'name' => 'ls',
        'cmd' => "ls -al",
        'out' => '/tmp/php_crontab.log',
        'err' => '/tmp/php_crontab.log',
        'time' => '* * * * *',
        'user' => 'www',
        'group' => 'www'
    ],
    [
        'name' => 'ls',
        'cmd' => "ls -al",
        'out' => '/tmp/php_crontab.log',
        'err' => '/tmp/php_crontab.log',
        'time' => '* * * * *',
        'user' => 'www',
        'group' => 'www'
    ],
];

$daemon = new \Jenner\Crontab\Daemon($missions);
$daemon->start();

Or use the task loader

function task_loader() {
    $missions = [
        [
            'name' => 'ls',
            'cmd' => "ls -al",
            'out' => '/tmp/php_crontab.log',
            'time' => '* * * * *',
            'user' => 'www',
            'group' => 'www'
        ],
        [
            'name' => 'ls',
            'cmd' => "ls -al",
            'out' => '/tmp/php_crontab.log',
            'time' => '* * * * *',
            'user' => 'www',
            'group' => 'www'
        ],
    ];

    return $missions;
}

$daemon = new \Jenner\Crontab\Daemon();
$daemon->registerTaskLoader("task_loader");
$daemon->start();

run as a daemon and start the http server

$missions = [
    [
        'name' => 'ls',
        'cmd' => "ls -al",
        'out' => '/tmp/php_crontab.log',
        'err' => '/tmp/php_crontab.log',
        'time' => '* * * * *',
        'user' => 'www',
        'group' => 'www'
    ],
    [
        'name' => 'ls',
        'cmd' => "ls -al",
        'out' => '/tmp/php_crontab.log',
        'err' => '/tmp/php_crontab.log',
        'time' => '* * * * *',
        'user' => 'www',
        'group' => 'www'
    ],
];

$http_daemon = new \Jenner\Crontab\HttpDaemon($missions, "php_crontab.log");
$http_daemon->start($port = 6364);

Then you can manage the crontab task by curl like:

curl http://127.0.0.1:6364/get_by_name?name=ls
curl http://127.0.0.1:6364/remove_by_name?name=hostname
curl http://127.0.0.1:6364/get

run the script

[root@jenner php_crontab]# ./bin/php_crontab 
php_crontab help:
-c  --config    crontab tasks config file
-p  --port      http server port
-f  --pid-file  daemon pid file
-l  --log       crontab log file
[root@jenner php_crontab]#nohup ./bin/php_crontab -c xxoo.php -p 8080 -f /var/php_crontab.pid -l /var/logs/php_crontab.log >/dev/null & 

blog:www.huyanping.cn

php_crontab's People

Contributors

white-poto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php_crontab's Issues

建议:动态获取Mission

只是个建议

在以daemon方式运行时,Mission是在start方法的定时器外部建立的,后续循环无法动态修改,建议在定时器内部建立,这样当Mission有变动时,不需要重启daemon

    /**
     * start crontab and loop
     */
    public function start()
    {
        $this->logger->info("crontab start");
        $crontab = $this->createCrontab(); // 外部建立Mission
        $loop = Factory::create();
        // add periodic timer
        $loop->addPeriodicTimer(60, function () use ($crontab, $loop) {
            $loop->addTimer(60 - time() % 60, function () use ($crontab) {
                $pid = pcntl_fork();
                if ($pid > 0) {
                    return;
                } elseif ($pid == 0) {
                    $crontab->start(time());
                    exit();
                } else {
                    $this->logger->error("could not fork");
                }
            });
        });
//例如

        $loop->addPeriodicTimer(60, function () use ($loop) {
            $loop->addTimer(60 - time() % 60, function() {
                //  get missions from outside
                $tasks = call_user_func($this->mission_callback);
                $this->setTasks($tasks);
                $crontab = $this->createCrontab();
                $pid = pcntl_fork();
                if ($pid > 0) {
                    return;
                } elseif ($pid == 0) {
                    $crontab->start(time());
                    exit();
                } else {
                    $this->logger->error("could not fork");
                    exit();
                }
            });
        });

P.s 好像Daemon.php是ANSI编码

关于Mission执行超时设定

Process默认是60,不过有些Crontab可能执行时期较长,目前我是手动在Misssion里改成0,最好能够在外部指定

    /**
     * start mission process
     */
    public function run()
    {
        $out = $this->out;
        $err = $this->err;
        // Todo 最后一个参数代表超时,默认为60,改成0
        $process = new \Symfony\Component\Process\Process($this->cmd, null, null, null ,0);
        $process->run(function ($type, $buffer) use ($out, $err) {
            if ($type == \Symfony\Component\Process\Process::ERR) {
                $err->error($buffer);
            } else {
                echo "run task:" . $this->name() . "\n";
                $out->info($this->name);
                $out->info($buffer);
            }
        });
    }

测试启动example中的http_daemon.php报参数类型错误

错误如下:
[root@localhost example]# php http_daemon.php
PHP Fatal error: Uncaught TypeError: Argument 1 passed to Jenner\Crontab\HttpDaemon::crontabCallback() must be an instance of Jenner\Crontab\Crontab, instance of React\EventLoop\Timer\Timer given in /root/vendor/jenner/crontab/src/Jenner/Crontab/HttpDaemon.php:58
Stack trace:
#0 [internal function]: Jenner\Crontab\HttpDaemon->crontabCallback(Object(React\EventLoop\Timer\Timer))
#1 /root/vendor/jenner/crontab/vendor/react/event-loop/Timer/Timers.php(90): call_user_func(Array, Object(React\EventLoop\Timer\Timer))
#2 /root/vendor/jenner/crontab/vendor/react/event-loop/StreamSelectLoop.php(177): React\EventLoop\Timer\Timers->tick()
#3 /root/vendor/jenner/crontab/src/Jenner/Crontab/HttpDaemon.php(52): React\EventLoop\StreamSelectLoop->run()
#4 /root/vendor/jenner/crontab/example/http_daemon.php(34): Jenner\Crontab\HttpDaemon->start()
#5 {main}

thrown in /root/vendor/jenner/crontab/src/Jenner/Crontab/HttpDaemon.php on line 58

使用报错

Call to undefined function Jenner\SimpleFork\pcntl_signal()
错误位置
FILE: /home/wwwroot/vendor/jenner/simple_fork/src/Process.php  LINE: 234

Jenner\Crontab\Mission运行前未设置要运行的用户组以及用户

修改如下,不知道合适不合适

   /**
     * start mission process
     */
    public function run()
    {
        // set user and group
        $this->setUserAndGroup();
        $out = $this->out;
        $err = $this->err;
        $process = new \Symfony\Component\Process\Process($this->cmd);
        $process->run(function ($type, $buffer) use ($out, $err) {
            if ($type == \Symfony\Component\Process\Process::ERR) {
                $err->error($buffer);
            } else {
                $out->info($buffer);
            }
        });
    }

PHP Catchable fatal error: Argument 1 passed to Jenner\Crontab\HttpDaemon::crontabCallback()

PHP Notice: Undefined index: help: in /data/app/task/php_crontab/bin/crontab.php on line 193
PHP Notice: Undefined index: config in /data/app/task/php_crontab/bin/crontab.php on line 191
PHP Notice: Undefined index: port in /data/app/task/php_crontab/bin/crontab.php on line 191
PHP Notice: Undefined index: pid-file in /data/app/task/php_crontab/bin/crontab.php on line 191
PHP Notice: Undefined index: log in /data/app/task/php_crontab/bin/crontab.php on line 191
PHP Catchable fatal error: Argument 1 passed to Jenner\Crontab\HttpDaemon::crontabCallback() must be an instance of Jenner\Crontab\Crontab, instance of React\EventLoop\Timer\Timer given in /data/app/task/php_crontab/src/Jenner/Crontab/HttpDaemon.php on line 58

任务配置中out参数处理问题

看你的demo,out参数对应的应该是脚本执行日志吧,getProtocol中做了如下处理:

    /**
     *
     * @param $stream
     * @return mixed
     */
    protected static function getProtocol($stream)
    {
        if(strpos($stream, 'unix') === 0){
            return 'unix';
        }
        $stream_info = parse_url($stream);
        if (!array_key_exists('scheme', $stream_info)) {
            throw new \InvalidArgumentException("stream format error");
        }

        return $stream_info['scheme'];
    }

这样一来就抛异常了呀

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.