Rumah > Artikel > rangka kerja php > Pemahaman mendalam tentang mekanisme penjadualan tugas berjadual Laravel
Artikel ini membawakan anda pengetahuan yang berkaitan tentang laravel mekanisme penjadualan tugasan berjadual Ia terutamanya memperkenalkan logik pelaksanaan asas, operasi latar belakang dan isu yang berkaitan untuk mencegah pertindihan.
[Cadangan berkaitan: tutorial video laravel]
Web yang kompleks latar belakang sistem, mesti ada banyak skrip atau tugas yang dijadualkan untuk dijalankan.
Sebagai contoh, sistem perangkak perlu merangkak beberapa data tapak web dengan kerap, sistem pembayaran balik pinjaman automatik perlu memotong dan menyelesaikan akaun pengguna secara berkala setiap bulan, dan sistem keahlian perlu kerap mengesan baki hari keahlian pengguna. untuk Pemberitahuan segera tentang pembaharuan, dsb. Crontab terbina dalam dalam sistem Linux biasanya digunakan secara meluas untuk menjalankan tugas berjadual. Format arahan tugasan adalah seperti berikut:
penjelasan arahan crontab
Command line crontab -e untuk memasuki penyuntingan crontab, edit arahan yang ingin anda laksanakan, simpan dan keluar Ia akan berkuat kuasa.
Walau bagaimanapun, artikel ini tidak akan membincangkan kandungan crontab terlalu banyak, tetapi akan memberikan analisis yang mendalam tentang cara rangka kerja PHP Laravel merangkum modul penjadualan tugas (Penjadualan Tugas) yang lebih berkuasa berdasarkan crontab.
Untuk tugas berjadual, sudah tentu kita boleh mengkonfigurasi perintah crontab untuk setiap tugas. Walau bagaimanapun, jika anda melakukan ini, apabila bilangan tugas yang dijadualkan meningkat, arahan crontab juga akan meningkat secara linear.
Lagipun, crontab ialah konfigurasi peringkat sistem Untuk menjimatkan mesin dalam perniagaan, kami sering meletakkan berbilang projek kecil pada pelayan yang sama c
arahan rontab Jika terdapat terlalu banyak , ia akan menjadi mudah untuk menguruskan huru-hara, dan fungsinya tidak cukup fleksibel dan berkuasa (tidak boleh berhenti dan bermula sesuka hati, mengendalikan kebergantungan antara tugas, dsb.).
Penyelesaian Laravel untuk masalah ini adalah dengan mengisytiharkan hanya satu crontab Semua tugasan berjadual dalam perniagaan diproses dan dinilai dalam crontab ini, untuk mengurus tugas pada tahap kod:
Iaitu, php artisan schedule:run berjalan sekali setiap minit (frekuensi tertinggi crontab bagi konfigurasi tugas khusus dalam perniagaan, ia didaftarkan dalam Kernel::schedule()
* * * * * php artisan schedule:run >> /dev/null 2>&1
Dalam contoh di atas, kita boleh Amat jelas bahawa tiga tugas berjadual didaftarkan dalam sistem, dan kaedah semantik seperti everyMinute, everyFifteenMinutes, harian, dan setiap jam disediakan untuk mengkonfigurasi kitaran tugas.
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任务 } }Pada asasnya, kaedah semantik ini hanyalah nama lain untuk perwakilan crontab, dan akhirnya akan ditukar kepada ungkapan dalam crontab (seperti * * * * * bermaksud pelaksanaan sekali setiap minit). Dengan cara ini, perintah php artisan schedule:run yang dilaksanakan sekali setiap minit akan mengimbas semua arahan yang didaftarkan dalam Kernel::schedule dan menentukan bahawa kitaran pelaksanaan yang dikonfigurasikan untuk arahan itu telah tamat tempoh, Jika ia tamat tempoh, tolaknya ke baris gilir pelaksanaan yang belum selesai. Akhir sekali laksanakan semua arahan mengikut urutan.
// ScheduleRunCommand::handle函数 public function handle() { foreach ($this->schedule->dueEvents() as $event) { if (! $event->filtersPass()) { continue; } $event->run(); } }
jadual carta alir tugas
Dua perkara perlu dinyatakan di sini Pertama, cara menilai sama ada arahan itu telah dan sepatutnya dilaksanakan. Kedua, isu perintah pelaksanaan arahan. Pertama sekali, masa pelaksanaan yang ditentukan oleh ungkapan crontab merujuk kepada masa mutlak, bukan masa relatif. Jadi hanya berdasarkan masa semasa dan ungkapan crontab, boleh menentukan sama ada arahan telah tamat dan harus dilaksanakan. Jika anda ingin melaksanakan masa relatif, anda mesti menyimpan masa pelaksanaan terakhir, dan kemudian anda boleh mengira bila pelaksanaan seterusnya sepatutnya. Perbezaan antara masa mutlak dan masa relatif boleh diringkaskan dengan gambar berikut (masa pelaksanaan crontab ditunjukkan dalam senarai di sebelah kiri gambar). Laravel menggunakan perpustakaan ekspresi cron (github.com/mtdowling/cron-expression) untuk analisis statik dan pertimbangan ekspresi crontab. Prinsip ini juga agak intuitif, iaitu analisis dan perbandingan aksara statik.crontab ialah masa mutlak, bukan masa relatif
Masalah kedua ialah perintah pelaksanaan Kita boleh lihat dari gambar sebelumnya bahawa jika anda Berbilang tugasan didaftarkan dalam kaedah Kernel::schedule Biasanya, ia dilaksanakan secara berurutan. Maksudnya, Tugasan 2 tidak akan memulakan pelaksanaan sehingga Tugasan 1 selesai. Dalam kes ini, jika Tugasan 1 sangat memakan masa, ia akan menjejaskan pelaksanaan Tugasan 2 mengikut masa, yang memerlukan perhatian khusus semasa pembangunan. Walau bagaimanapun, menambah runInBackground semasa mendaftarkan tugasan dalam Kernel::schedule boleh merealisasikan pelaksanaan latar belakang tugasan Kami akan membincangkan perkara ini secara terperinci di bawah. 2. Operasi latar belakang Disebabkan ciri pelaksanaan berurutan baris gilir tugas yang dijadualkan yang disebutkan di atas, jika masa pelaksanaan tugas sebelumnya terlalu lama, ia akan menghalang pelaksanaan tepat pada masanya daripada tugasan seterusnya. Untuk menyelesaikan masalah ini, Laravel menyediakan kaedah runInBackground untuk melaksanakan tugas di latar belakang. Seperti:// 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,来确定需要执行哪个任务的后置函数。代码如下:
// 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); }
有些定时任务指令需要执行很长时间,而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); }
我们知道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视频教程】
Atas ialah kandungan terperinci Pemahaman mendalam tentang mekanisme penjadualan tugas berjadual Laravel. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!