In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article focuses on "how to understand the Laravel timed task scheduling mechanism", interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "how to understand the Laravel timed task scheduling mechanism".
1. Basic implementation logic
In the background of a complex web system, there must be a lot of scheduled scripts or tasks to run.
For example, the crawler system needs to crawl some website data regularly, and the automatic loan repayment system needs to deduct money from the user's account at a fixed time every month.
The member system needs to check the remaining member days of the user regularly in order to notify the renewal in time, and so on. The crontab built into the Linux system is generally widely used for running timing tasks.
Crontab instruction interpretation
The command line crontab-e goes into crontab editing, and after editing the instructions you want to execute, save and exit will take effect.
However, this article will not talk too much about crontab, but will take an in-depth analysis of how the PHP Laravel framework encapsulates a more powerful task scheduling (Task Scheduling) module based on crontab.
For scheduled tasks, we can of course configure one crontab instruction per task. It's just that in doing so, crontab instructions grow linearly as the number of scheduled tasks increases.
After all, crontab is a system-level configuration. In order to save machines in business, we often put multiple projects on the same server for a small number of projects, c
With too many rontab instructions, it is easy to manage confusion, and the functionality is not flexible and powerful (unable to shut down at will, dealing with dependencies between tasks, etc.).
The solution to this Laravel is to declare only one crontab, and all scheduled tasks in the business are processed and judged in this crontab to manage tasks at the code level:
* php artisan schedule:run > > / dev/null 2 > & 1
That is, php artisan schedule:run runs every minute (the highest frequency of crontab). As for the specific task configuration on the business, it is registered in Kernel::schedule ().
Class Kernel extends ConsoleKernel {Protected function schedule (Schedule $schedule) {$schedule- > command ('account:check')-> everyMinute (); / execute the php artisan account:check instruction $schedule- > exec (' node / home/username/index.js')-> everyFifteenMinutes () every minute; / / execute the node / home/username/index.js command $schedule- > job (new MyJob ())-> cron ('1 2 3 10 *') every 15 minutes / / distribute a MyJob task to the task queue at 02:01 on October 3 every year}}
In the above example, we can clearly see that three scheduled tasks are registered in the system, and provide semantic methods such as everyMinute, everyFifteenMinutes, daily, hourly and so on to configure the task cycle.
In essence, these semantic methods are just a nickname for the crontab representation, which will eventually be translated into expressions in crontab (such as * for execution every minute).
In this way, a php artisan schedule:run instruction that is executed every minute scans all instructions registered in Kernel::schedule and determines that the execution cycle configured for that instruction has expired
If it expires, it is pushed into the queue to be executed. Finally, all the instructions are executed in turn.
/ / ScheduleRunCommand::handle function public function handle () {foreach ($this- > schedule- > dueEvents () as $event) {if (! $event- > filtersPass ()) {continue;} $event- > run ();}}
There are two points to pay attention to here. First, how to determine whether the instruction has been Due and should be executed. Second, the order of execution of instructions.
First, the execution time specified by the crontab expression refers to the absolute time, not the relative time. So just based on the current time and the crontab expression
You can determine whether the instruction has been Due and it is time to execute. If you want to achieve the relative time, you must store the time of the last execution
Then we can calculate when the next execution should be carried out. The difference between absolute time and relative time can be summarized in the following diagram (the execution time of crontab is shown in the list on the left side of the figure).
The static analysis and judgment of crontab expressions in Laravel uses cron-expression library, and the principle is relatively intuitive, that is, static character analysis and comparison.
Crontab is an absolute time, not a relative time
The second problem is the order of execution, as we can see in the previous figure, if you register multiple tasks in the Kernel::schedule method
Normally they are executed sequentially. That is, you must wait until Task 1 execution is complete before Task 2 execution begins.
In this case, if Task 1 is very time-consuming, it will affect the timely execution of Task 2, which is particularly important in development.
However, the background execution of a task can be achieved by registering a task in Kernel::schedule with runInBackground, which we will discuss in more detail below.
two。 Background operation
Due to the sequential execution of scheduled task queues mentioned earlier, the execution time of previous tasks is too long, which will hinder the timely execution of subsequent tasks.
To solve this problem, runInBackground is provided in Laravel to make the task execute in the background. Such as:
/ / Kernel.phpprotected function schedule (Schedule $schedule) {$schedule- > command ('test:hello') / / execute the command command: php artisan test:hello-> cron (' 10 11 1 * *') / / execute the command at 11:10:00 on the 1st of each month-> timezone ('Asia/Shanghai') / / set time zone-> before (function () {/ * do something*/}) / / pre-hook Execute this callback before command execution-> after (function () {/ * do something*/}) / / post hook, and execute this callback after command execution-> runInBackground () / / run this command in the background / / execute the command command every minute: php artisan test:world $schedule- > command ('test:world')-> everyMinute ();}
The principle of running in the background is actually very simple. We know that in linux systems, the command line instructions are followed by a "&" symbol to make the task execute in the background.
The inner principle of the runInBackground method is to add a "&" symbol to the last run instruction. But after the task is changed to background execution,
There is a new problem, that is, how to trigger the post-hook function of the task. Because the post hook function needs to be executed immediately after the task is finished.
So there must be a way to monitor the moment when the task running in the background ends. Let's find out from the source code
/ / build the command instruction protected function buildBackgroundCommand (Event $event) {$output = ProcessUtils::escapeArgument ($event- > output) running in the background; $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 &');}
The content of the $finished string is a hidden php artisan instruction, php artisan schedule:finish.
This command is appended to the command command that was supposed to be executed to detect and execute the post-hook function.
Php artisan schedule:finish source code is very simple, using mutex_name to uniquely identify a task to be executed
The post function of which task needs to be executed is determined by comparing the mutex_name of all tasks registered in the system. The code is as follows:
The source code of / / Illuminate/Console/Scheduling/ScheduleFinishCommand.php// php artisan schedule:finish instruction public function handle () {collect ($this- > schedule- > events ())-> filter (function ($value) {return $value- > mutexName () = = $this- > argument ('id');})-> each- > callAfterCallbacks ($this- > laravel);} 3. Prevent repetition
Some scheduled task instructions take a long time to execute, while laravel schedule tasks can run every minute at most.
This means that if the task itself does not end after running for more than 1 minute, then by the time the next minute comes, the same task will start to run.
This is probably the result we don't want to see. Therefore, it is necessary to think of a mechanism to avoid prevent overlapping of tasks at the same time.
This scenario is very similar to the situation of multi-process or multi-threaded programs grabbing resources, and a common way of prevention is to lock resources.
Specific to the laravel scheduled task, that is to lock the task, only after getting the task lock, can the specific content of the task be executed.
The withoutOverlapping method is provided in Laravel to avoid repetition of scheduled tasks. For the implementation of the specific lock, you need to implement the three interfaces defined in the Illuminate\ Console\ Scheduling\ Mutex.php interface:
Interface Mutex {/ / implement to create the lock interface public function create (Event $event); / / implement the interface public function exists (Event $event) to determine whether the lock exists; / / implement the unlocked interface public function forget (Event $event);}
Of course, this interface can be implemented by itself, and Laravel also gives a default implementation, that is, using the cache as the carrier for storing locks (see the Illuminate\ Console\ Scheduling\ CacheMutex.php file).
Between each run task, the program determines whether it needs to prevent repetition, and if it does, it no longer runs the task code:
/ / Illuminate\ Console\ Scheduling\ Event.phppublic function run () {/ / determine whether you need to prevent repetition. If you need to prevent repetition and the lock creation is not successful, it means that there is already a task running, then exit directly and no longer execute the specific task if ($this- > withoutOverlapping & &! $this- > mutex- > create ($this)) {return;} $this- > runInBackground?$this- > runCommandInBackground ($container): $this- > runCommandInForeground ($container);} 4. How to achieve a 30-second task?
We know that the finest granularity of crontab tasks can only be up to the minute level. So if I want to achieve a task that is executed once in 30s,
How do you need to achieve it? There is also some discussion on stackoverflow about this issue. It is suggested that you should write a sleep to implement it at the business level. The sample code is as follows:
Public function handle () {runYourCode (); / / run business code sleep (30); / / sleep for 30 seconds runYourCode (); / / run business code again}
If the runYourCode execution implementation is not too long, the above task is executed every 1min, which is actually equivalent to the runYourCode function executing every 30 seconds.
If the runYourCode function itself takes a long time to execute, then the sleep 30 seconds here will be less accurate.
Of course, instead of using Laravel's scheduled task system, we can use a special scheduled task scheduling open source tool to achieve the function that is executed every 30 seconds.
A scheduled task scheduling tool nomad is recommended here.
If you really want to use Laravel's own scheduled task system, and want to achieve a more accurate function of executing tasks every 30 seconds, then you can combine laravel's queue job to achieve it. As follows:
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 ();}}
Through the delay method of the Laravel queue function, we can delay the execution of tasks by 30 seconds, so if we dispatch two tasks to the queue every 1min, one of them has a delay of 30 seconds.
In addition, write the code runYourCode that you want to execute in the task, and you can achieve the function of executing once every 30 seconds. However, it should be noted that the anti-coincidence function of scheduling in this implementation is no longer effective.
You need to implement locking to prevent repetition in the business code runYourCode.
At this point, I believe you have a deeper understanding of "how to understand the Laravel timed task scheduling mechanism". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.