目次
1. 基本的な実装ロジック
スケジュールされたタスク キューの順次実行の前述の機能は、前のタスクの実行時間が長すぎる場合、後続のタスクの予定どおりの実行を妨げます。 。
3. 防止重复
4. 如何实现30秒任务?
ホームページ PHPフレームワーク Laravel Laravelのスケジュールされたタスクのスケジューリングメカニズムについての深い理解

Laravelのスケジュールされたタスクのスケジューリングメカニズムについての深い理解

Feb 23, 2022 pm 05:20 PM
laravel

この記事では、laravel スケジュールされたタスクのスケジューリング メカニズムに関する関連知識を提供します。主に、基本的な実装ロジック、バックグラウンド操作、および重複を防ぐための関連問題について紹介します。皆様のお役に立てれば幸いです。

Laravelのスケジュールされたタスクのスケジューリングメカニズムについての深い理解

[関連する推奨事項: laravel ビデオ チュートリアル]

1. 基本的な実装ロジック

複雑な Web Inシステムのバックグラウンドでは、実行されるスケジュールされたスクリプトやタスクが多数存在する必要があります。

たとえば、クローラー システムは一部の Web サイト データを定期的にクロールする必要があり、自動ローン返済システムは毎月定期的にユーザー アカウントの引き落としと決済を行う必要があり、

メンバーシップ システムは定期的にユーザーの会員残日数を把握し、更新等を速やかに通知します。 Linux システムの組み込み crontab は、スケジュールされたタスクを実行するために一般に広く使用されています。タスク コマンドの形式は次のとおりです。

crontab コマンドの説明

コマンド ライン crontab -e で crontab 編集に入り、実行するコマンドを編集します。保存して終了すると有効になります。

ただし、この記事では crontab の内容についてはあまり説明しませんが、PHP Laravel フレームワークが crontab に基づいたより強力なタスク スケジューリング (タスク スケジューリング) モジュールをどのようにカプセル化するかについて詳細な分析を提供します。

スケジュールされたタスクの場合、もちろん、タスクごとに crontab 命令を構成できます。ただし、これを行うと、スケジュールされたタスクの数が増加するにつれて、crontab 命令も直線的に増加します。

結局のところ、crontab はシステム レベルの構成です。ビジネスでマシンを節約するために、私たちはよく複数の小さなプロジェクトを同じサーバーに配置します。多くの c

rontab 命令があります。これがなければ、混乱を管理するのは簡単ですが、機能は十分に柔軟で強力ではありません(自由に停止および開始できない、タスク間の依存関係を処理できないなど)。

Laravel の解決策は、crontab を 1 つだけ宣言することです。ビジネス内のスケジュールされたタスクはすべてこの crontab で処理および判断され、コード レベルでタスクを管理します:

* * * * * php artisan schedule:run >> /dev/null 2>&1
ログイン後にコピー

それが php 職人ですSchedule:run は 1 分に 1 回実行されます (crontab の最高頻度) ビジネスにおける特定のタスク構成については、Kernel::schedule()

class Kernel extends ConsoleKernel
{
    Protected function schedule(Schedule $schedule)
    {
        $schedule->command('account:check')->everyMinute(); // 每分钟执行一次php artisan account:check 指令
        $schedule->exec('node /home/username/index.js')->everyFifteenMinutes(); //每15分钟执行一次node /home/username/index.js 命令
        $schedule->job(new MyJob())->cron('1 2 3 10 *'); // 每年的10月3日凌晨2点1分向任务队列分发一个MyJob任务
    }
}
ログイン後にコピー

に登録されています。例 3 つのスケジュールされたタスクがシステムに登録されており、タスク サイクルを構成するために、everyMinute、everyFifteenMinutes、daily、hourly などのセマンティック メソッドが提供されます。

本質的に、これらのセマンティック メソッドは crontab 表現の別名にすぎず、最終的には crontab の式に変換されます (* * * * * は 1 分ごとに実行を意味します)。

このように、1分ごとに実行されるphpArtisanのschedule:runコマンドは、Kernel::scheduleに登録されているすべてのコマンドをスキャンし、コマンドに設定された実行サイクルの期限が切れたことを判断します。有効期限が切れた場合は、保留中の実行キューにプッシュします。最後に、すべての命令を順番に実行します。

// ScheduleRunCommand::handle函数
public function handle()
{
    foreach ($this->schedule->dueEvents() as $event) {
        if (! $event->filtersPass()) {
            continue;
        }
        $event->run();
    }
}
ログイン後にコピー

スケジュールタスクのフローチャート

ここで注意すべき点は 2 つあり、1 つは、指示が期日を超えて実行されるべきかどうかを判断する方法です。第二に、命令の実行順序の問題です。

まず、crontab 式で指定される実行時間は、相対時間ではなく絶対時間を指します。したがって、

は、現在の時刻と crontab 式に基づいてのみ、命令の期限が切れているか、実行する必要があるかを判断できます。相対時間を実装する場合は、最後の実行時刻

を保存する必要があります。そうすれば、次の実行時刻を計算できます。絶対時間と相対時間の違いは次の図のように要約できます (crontab の実行時間は図の左側のリストに表示されます)。

Laravel では、crontab 式の静的解析と判定に cron-expression ライブラリ (github.com/mtdowling/cron-expression) を使用していますが、その原理も静的な文字解析と比較という比較的直感的なものです。

crontab は相対時間ではなく絶対時間です

2 番目の問題は実行順序です。前の図からわかるように、複数の時間を実行すると、タスクは Kernel::schedule メソッドに登録されます

通常、タスクは順番に実行されます。つまり、タスク 1 が完了するまでタスク 2 は実行を開始しません。

この場合、タスク 1 に非常に時間がかかると、タスク 2 の予定どおりの実行に影響するため、開発中に特別な注意が必要になります。

ただし、Kernel::schedule にタスクを登録する際に runInBackground を追加することで、タスクのバックグラウンド実行を実現できます。

2. バックグラウンド実行

スケジュールされたタスク キューの順次実行の前述の機能は、前のタスクの実行時間が長すぎる場合、後続のタスクの予定どおりの実行を妨げます。 。

この問題を解決するために、Laravel はバックグラウンドでタスクを実行するメソッド runInBackground を提供します。のように:###

// Kernel.php
protected function schedule(Schedule $schedule)
{
    $schedule->command('test:hello') // 执行command命令:php artisan test:hello
    ->cron('10 11 1 * *') // 每月1日的11:10:00执行该命令
    ->timezone('Asia/Shanghai') // 设置时区
    ->before(function(){/*do something*/}) // 前置hook,命令执行前执行此回调
    ->after(function(){/*do something*/}) // 后置钩子,命令执行完之后执行此回调
    ->runInBackground(); // 后台运行本命令
    // 每分钟执行command命令:php artisan test:world
    $schedule->command('test:world')->everyMinute();
}
ログイン後にコピー

后台运行的原理,其实也非常简单。我们知道在linux系统下,命令行的指令最后加个“&”符号,可以使任务在后台执行。

runInBackground方法内部原理其实就是让最后跑的指令后面加了“&”符号。不过在任务改为后台执行之后,

又有了一个新的问题,即如何触发任务的后置钩子函数。因为后置钩子函数是需要在任务跑完之后立即执行,

所以必须要有办法监测到后台运行的任务结束的一瞬间。我们从源代码中一探究竟(Illuminate/Console/Scheduling/CommandBuilder.php)

// 构建运行在后台的command指令
protected function buildBackgroundCommand(Event $event)
{
    $output = ProcessUtils::escapeArgument($event->output);
    $redirect = $event->shouldAppendOutput ? ' >> ' : ' > ';
    $finished = Application::formatCommandString('schedule:finish').' "'.$event->mutexName().'"';
    return $this->ensureCorrectUser($event,
        '('.$event->command.$redirect.$output.' 2>&1 '.(windows_os() ? '&' : ';').' '.$finished.') > '
        .ProcessUtils::escapeArgument($event->getDefaultOutput()).' 2>&1 &'
    );
}
ログイン後にコピー

$finished字符串的内容是一个隐藏的php artisan指令,即php artisan schedule:finish

该命令被附在了本来要执行的command命令后面,用来检测并执行后置钩子函数。

php artisan schedule:finish 的源代码非常简单,用mutex_name来唯一标识一个待执行任务,

通过比较系统中注册的所有任务的mutex_name,来确定需要执行哪个任务的后置函数。代码如下:

// Illuminate/Console/Scheduling/ScheduleFinishCommand.php
// php artisan schedule:finish指令的源代码
public function handle()
{
    collect($this->schedule->events())->filter(function ($value) {
        return $value->mutexName() == $this->argument('id');
    })->each->callAfterCallbacks($this->laravel);
}
ログイン後にコピー

3. 防止重复

有些定时任务指令需要执行很长时间,而laravel schedule任务最频繁可以做到1分钟跑一次。

这也就意味着,如果任务本身跑了1分钟以上都没有结束,那么等到下一个1分钟到来的时候,又一个相同的任务跑起来了。

这很可能是我们不想看到的结果。因此,有必要想一种机制,来避免任务在同一时刻的重复执行(prevent overlapping)。

这种场景非常类似多进程或者多线程的程序抢夺资源的情形,常见的预防方式就是给资源加锁。

具体到laravel定时任务,那就是给任务加锁,只有拿到任务锁之后,才能够执行任务的具体内容。

Laravel中提供了withoutOverlapping方法来让定时任务避免重复。具体锁的实现上,需要实现Illuminate\Console\Scheduling\Mutex.php接口中所定义的三个接口:

interface Mutex
{
    // 实现创建锁接口
    public function create(Event $event);
    // 实现判断锁是否存在的接口
    public function exists(Event $event);
    // 实现解除锁的接口
    public function forget(Event $event);
}
ログイン後にコピー

该接口当然可以自己实现,Laravel也给了一套默认实现,即利用缓存作为存储锁的载体(可参考Illuminate\Console\Scheduling\CacheMutex.php文件)。

在每次跑任务之间,程序都会做出判断,是否需要防止重复,如果重复了,则不再跑任务代码:

// Illuminate\Console\Scheduling\Event.php
public function run()
{
    // 判断是否需要防止重复,若需要防重复,并且创建锁不成功,则说明已经有任务在跑了,这时直接退出,不再执行具体任务
    if ($this->withoutOverlapping && ! $this->mutex->create($this)) {
        return;
    }
    $this->runInBackground?$this->runCommandInBackground($container):$this->runCommandInForeground($container);
}
ログイン後にコピー

4. 如何实现30秒任务?

我们知道crontab任务最精细的粒度只能到分钟级别。那么如果我想实现30s执行一次的任务,

需要如何实现?关于这个问题,stackoverflow上面也有一些讨论,有建议说在业务层面实现,自己写个sleep来实现,示例代码如下:

public function handle()
{
    runYourCode(); // 跑业务代码
    sleep(30); // 睡30秒
    runYourCode(); // 再跑一次业务代码
}
ログイン後にコピー

如果runYourCode执行实现不太长的话,上面这个任务每隔1min执行一次,其实相当于runYourCode函数每30秒执行一次。

如果runYourCode函数本身执行时间比较长,那这里的sleep 30秒会不那么精确。

当然,也可以不使用Laravel的定时任务系统,改用专门的定时任务调度开源工具来实现每隔30秒执行一次的功能,

在此推荐一个定时任务调度工具nomad(https://github.com/hashicorp/nomad)。

如果你确实要用Laravel自带的定时任务系统,并且又想实现更精确一些的每隔30秒执行一次任务的功能,那么可以结合laravel 的queue job来实现。如下:

public function handle()
{
    $job1 = (new MyJob())->onQueue(“queue-name”);
    $job2 = (new MyJob())->onQueue(“queue-name”)->delay(30);
    dispatch($job1);
    dispatch($job2):
}

class MyJob implement Illuminate\Contracts\Queue\ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
    public function handle()
    {
        runYourCode();
    }
}
ログイン後にコピー

通过Laravel 队列功能的delay方法,可以将任务延时30s执行,因此如果每隔1min,我们都往队列中dispatch两个任务,其中一个延时30秒。

另外,把自己要执行的代码runYourCode写在任务中,即可实现30秒执行一次的功能。不过这里需要注意的是,这种实现中scheduling的防止重合功能不再有效,

需要自己在业务代码runYourCode中实现加锁防止重复的功能。

以上,就是使用Laravel Scheduling定时任务调度的原理分析和注意事项。作为最流行的PHP框架,Laravel大而全,

组件基本包含了web开发的各方面需求。其中很多组件的实现思想,还是很值得深入源码一探究竟的。

【相关推荐:laravel视频教程

以上がLaravelのスケジュールされたタスクのスケジューリングメカニズムについての深い理解の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHP でオブジェクト リレーショナル マッピング (ORM) を使用してデータベース操作を簡素化するにはどうすればよいですか? PHP でオブジェクト リレーショナル マッピング (ORM) を使用してデータベース操作を簡素化するにはどうすればよいですか? May 07, 2024 am 08:39 AM

PHP でのデータベース操作は、オブジェクトをリレーショナル データベースにマップする ORM を使用して簡素化されます。 Laravel の EloquentORM を使用すると、オブジェクト指向構文を使用してデータベースと対話できます。モデル クラスを定義したり、Eloquent メソッドを使用したり、実際にブログ システムを構築したりすることで ORM を使用できます。

Laravel と CodeIgniter の最新バージョンの比較 Laravel と CodeIgniter の最新バージョンの比較 Jun 05, 2024 pm 05:29 PM

Laravel 9 と CodeIgniter 4 の最新バージョンでは、更新された機能と改善が提供されます。 Laravel9はMVCアーキテクチャを採用しており、データベース移行、認証、テンプレートエンジンなどの機能を提供します。 CodeIgniter4 は、HMVC アーキテクチャを使用してルーティング、ORM、およびキャッシュを提供します。パフォーマンスの面では、Laravel9 のサービスプロバイダーベースの設計パターンと CodeIgniter4 の軽量フレームワークにより、優れたパフォーマンスが得られます。実際のアプリケーションでは、Laravel9 は柔軟性と強力な機能を必要とする複雑なプロジェクトに適しており、CodeIgniter4 は迅速な開発や小規模なアプリケーションに適しています。

Laravel - アーティザンコマンド Laravel - アーティザンコマンド Aug 27, 2024 am 10:51 AM

Laravel - アーティザン コマンド - Laravel 5.7 には、新しいコマンドを処理およびテストするための新しい方法が付属しています。これには職人コマンドをテストする新しい機能が含まれており、そのデモについては以下で説明します。

Laravel と CodeIgniter のデータ処理機能はどのように比較されますか? Laravel と CodeIgniter のデータ処理機能はどのように比較されますか? Jun 01, 2024 pm 01:34 PM

Laravel と CodeIgniter のデータ処理機能を比較します。 ORM: Laravel はクラスとオブジェクトのリレーショナル マッピングを提供する EloquentORM を使用しますが、CodeIgniter は ActiveRecord を使用してデータベース モデルを PHP クラスのサブクラスとして表します。クエリビルダー: Laravel には柔軟なチェーンクエリ API がありますが、CodeIgniter のクエリビルダーはよりシンプルで配列ベースです。データ検証: Laravel はカスタム検証ルールをサポートする Validator クラスを提供しますが、CodeIgniter には組み込みの検証関数が少なく、カスタム ルールの手動コーディングが必要です。実践例:ユーザー登録例はLarを示しています

Laravel と CodeIgniter ではどちらが初心者に優しいでしょうか? Laravel と CodeIgniter ではどちらが初心者に優しいでしょうか? Jun 05, 2024 pm 07:50 PM

初心者にとって、CodeIgniter は学習曲線が緩やかで機能は少ないですが、基本的なニーズはカバーしています。 Laravel は幅広い機能セットを提供しますが、学習曲線はわずかに急になります。パフォーマンスの点では、Laravel と CodeIgniter はどちらも良好なパフォーマンスを示します。 Laravel にはより広範なドキュメントとアクティブなコミュニティ サポートがあり、CodeIgniter はよりシンプルで軽量で、強力なセキュリティ機能を備えています。ブログアプリケーションを構築する実際のケースでは、Laravel の EloquentORM を使用するとデータ操作が簡素化されますが、CodeIgniter ではより手動の構成が必要になります。

Laravel と CodeIgniter: 大規模プロジェクトにはどちらのフレームワークが適していますか? Laravel と CodeIgniter: 大規模プロジェクトにはどちらのフレームワークが適していますか? Jun 04, 2024 am 09:09 AM

大規模プロジェクト用のフレームワークを選択する場合、Laravel と CodeIgniter にはそれぞれ独自の利点があります。 Laravel はエンタープライズレベルのアプリケーション向けに設計されており、モジュール設計、依存関係の注入、強力な機能セットを提供します。 CodeIgniter は、速度と使いやすさを重視した、小規模から中規模のプロジェクトに適した軽量フレームワークです。複雑な要件と多数のユーザーを伴う大規模なプロジェクトには、Laravel のパワーとスケーラビリティがより適しています。単純なプロジェクトやリソースが限られている状況では、CodeIgniter の軽量で迅速な開発機能がより理想的です。

PHPコードの単体テストと統合テスト PHPコードの単体テストと統合テスト May 07, 2024 am 08:00 AM

PHP 単体テストおよび統合テスト ガイド 単体テスト: コードまたは関数の単一単位に焦点を当て、PHPUnit を使用して検証用のテスト ケース クラスを作成します。統合テスト: 複数のコードユニットがどのように連携するかに注意し、PHPUnit の setUp() メソッドと TearDown() メソッドを使用してテスト環境をセットアップおよびクリーンアップします。実際のケース: PHPUnit を使用して、データベースの作成、サーバーの起動、テストコードの作成など、Laravel アプリケーションの単体テストと統合テストを実行します。

Laravel と CodeIgniter: 小規模プロジェクトにはどちらのフレームワークが適していますか? Laravel と CodeIgniter: 小規模プロジェクトにはどちらのフレームワークが適していますか? Jun 04, 2024 pm 05:29 PM

小規模なプロジェクトの場合、Laravel は強力な機能とセキュリティを必要とする大規模なプロジェクトに適しています。 CodeIgniter は、軽量さと使いやすさを必要とする非常に小規模なプロジェクトに適しています。

See all articles