承接 monolyth/croney 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

monolyth/croney

Composer 安装命令:

composer require monolyth/croney

包简介

PHP-based CRON scheduler

README 文档

README

PHP-based CRON scheduler

Don't you hate having to juggle a gazillion cronjobs for each application? We sure do! We also hate having to adhere to a library-specific code format to circumvent this problem (e.g. Symfony, Laravel... you know who you are).

You know what we'd like to do? We just want to register a bunch of callables and have a central script figure it out. Hello Croney!

Installation

Composer (recommended)

composer require monomelodies/croney

Manual

  1. Download or clone the repository;
  2. Add the namespace Croney for the path path/to/croney/src to your PSR-4 autoloader.

Setting up the executable

Croney needs to run periodically, so create a simple executable that we will add as a cronjob:

#!/usr/bin/php
<?php

// Let's assume this is in bin/cron.
// It's empty for now.
$ chmod a+x bin/cron
$ crontab -e

How often you let Croney run is up to you. The default assumption is every minute since it is the smallest interval possible on Unix-like systems. We'll see how to optimise this to e.g. every five minutes later on. For now, register the cronjob with * * * * *, i.e. every minute.

The Scheduler class

At Croney's core is an instance of the Scheduler class. This is what is used to run tasks and it takes care (as the name implies) of scheduling them.

In your bin/cron file:

#!/usr/bin/php
<?php

use Croney\Scheduler;

$schedule = new Scheduler;

Adding tasks

The Scheduler extends ArrayObject, so to add a task simply set it. The simplest way is to add a callable:

#!/usr/bin/php
<?php

// ...
$schedule['some-task'] = function () {
    // ...perform the task...
};

This task gets run every minute (or whatever interval you set your cronjob to). A task can be any callable, including class methods (even static ones).

You can also pass a class name, which will then get instantiated and __invoke'd. If you need to pass construction arguments, do the instantiating yourself.

When you've setup all your tasks, call process on the Scheduler to actually run them:

<?php

// ...
$schedule->process();

Running tasks at specific intervals or times

To have more control over when exactly a task is run, add the Monolyth\Croney\RunAt attribute to your invokable:

<?php

$scheduler['some-task'] =
    #[Monolyth\Croney\RunAt("Y-m-d H:m")]
    function () {
        // ...
    };

The parameter to at is a PHP date string which, when parsed using the run's current time, should preg_match it. The above example runs the task every minute (which is the default assuming your cronjob runs every minute). To run a task every five minutes instead, you'd write something like this:

<?php

$scheduler['some-task'] =
    #[Monolyth\Croney\RunAt("Y-m-d H:[0-5][05]")]
    function () {
        // ...
    };

Note that the date function works with placeholders, so if you need to regex on e.g. a decimal (\d) you would need to double-escape it. See the PHP manual page for date for a list of all valid placeholders.

preg_match is called without checking string position, i.e. if you would pass only 'H' as the date to match it would run on the hour, but also every minute (since 00-24 are all valid minutes and it would match 'i' as well) and also every day, month and (for all practical purposes since I'm not expecting this library to still be alive by the year 2399 ;)) all years. So be as specific as you need!

Note that the seconds part is irrelevant due to the granularity of cron and should be omitted or your task will likely never run (since the date it is compared to also doesn't include seconds).

Also note that if your task is an __invokeable object, the RunAt attribute should be on the __invoke method, not the object itself.

Running the script less often

We mentioned earlier how you can also choose to run the cronjob less often than every minute, say every five minutes. If you only have tasks that run every five minutes (or multiples of that), that's fine and no further configuration is required. But what if you want to run your cronjob every five minutes, but still be able to schedule tasks based on minutes?

An example of this would be a cronjob that runs every five minutes, defining five tasks, each of which is run one minute after the previous task.

The first parameter when constructing the Scheduler object is actually its duration (in minutes):

<?php

$scheduler = new Scheduler(5); // Runs for five minutes

(As you'll have guessed, the default value here is 1.)

When you call process, the tasks will actually be run 5 times (once every minute) and executed when the time is there. E.g.:

<?php

$scheduler = new Scheduler(5);

// First task, runs only on the first loop
$scheduler['first-task'] =
    #[RunAt("H:[0-5]0")]
    function () {
        // ...
    };
// Second task, runs only on the second loop
$scheduler['second-task']
    #[RunAt("")]
    = function () {
        $this->at('H:[0-5]1');
    };
// etc.

Croney calls PHP's sleep function in between loops.

Croney tries to calculate the actual number of seconds to sleep, so if the tasks from the first loop took, say, 3 seconds in total it sleeps for 57 seconds before the next loop. Note however that this is not exact and does not guarantee that your task will run exactly on the dot. If your task involves time-based operations make sure to "round down" the time to the expected value.

In theory, you could let your script run at midnight on January the first and calculate everything from there. In the real world, this is obviously not practical since any error whatsoever means you have to wait a whole year to see if your fix solved the problem!

Typical values are every 5 or 10 minutes, maybe 30 or 60 on very busy servers.

Long running tasks

Typically a task runs in (micro)seconds, but sometimes one of your tasks will be "long running". If this is intentional (e.g. a periodic cleanup operation of user-uploaded files) you would obviously runAt it at a safe interval, and you should take care limit stuff in your task itself (e.g. "max 100 files per run"). Still, every so often you'll need to write a task that should run often, but might in extreme cases take longer than expected to do so.

A fictional example: a task that reads a mailbox (e.g. to push them into a ticketing system). If that mailbox explodes for whatever reason (let's be positive and imagine your application became really popular overnight ;)) this would pose a problem: the previous run might still be reading mails as the next run starts, causing mails to be handled twice. Obviously not desirable.

Croney "locks" each task prior to running, and does not attempt to re-run as long as it is locked. If a run fails due to locking, a warning is logged and the task is retried periodically for as long as the cronjob runs (assuming its RunAt configuration allows that).

The locking is done based on an MD5 hash of the task name. This includes the current working directory, so multiple Croney instances for different projects shouldn't conflict.

Error handling

You can pass an instance of Psr\Log\LoggerInterface as an argument to the Scheduler constructor. This will then be used to log any messages triggered by tasks, in the way that you specified.

If no logger was defined, all messages go to STDOUT or STDERR (see the included ErrorLogger).

Should your individual tasks also need logging, you'll need to supply them with their own instance of a logger.

Development

During development, you probably want to run tasks when testing (not just at a specific time), and also probably just a specific task. Croney as of version 0.3 comes with two command line flags for this:

--all|-a Use this flag to run all tasks, regardless of specified scheduling. Do not do this in production!

--job=jobname|-jjobname Run only the specified jobname. If the job is scheduled for particular times, you'll likely want to use this in conjunction with the --all (or -a) flag.

You might also want to receive some more verbose feedback on what's going on. To accomplish this, call your executable with the --verbose (or -v) flag.

If you need to test tasks in a longer running schedule, it's annoying to have to wait minutes before your tasks complete. In that case, simply override the scheduler's internal sleeper property. An example (that is actually used in the unit tests for Croney itself):

<?php

$scheduler = new class (2) extends Scheduler {
    public function __construct(int $duration)
    {
        parent::__construct($duration);
        $this->sleeper = new class () extends Sleeper {
            public function snooze(int $seconds) : void
            {
                // parent implementation: simply `sleep($seconds)`
                // next call is needed to realign the internal timer
                // so RunAt attributes keep working.
                $this->advanceInternalClock();
            }
        };
    }
};

Croney also includes a TestLogger which simply echoes logged messages, so you may use output buffering to check them in tests.

monolyth/croney 适用场景与选型建议

monolyth/croney 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 444 次下载、GitHub Stars 达 0, 最近一次更新时间为 2016 年 07 月 10 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 monolyth/croney 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 monolyth/croney 我们能提供哪些服务?
定制开发 / 二次开发

基于 monolyth/croney 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 444
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 12
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2016-07-10