怎么理解Laravel定时任务调度机制


本篇内容主要讲解“怎么理解Laravel定时任务调度机制”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解Laravel定时任务调度机制”吧!一个复杂的web系统后台当中,一定会有很多定时脚本或者任务要跑。例如爬虫系统需要定期去爬取一些网站数据,自动还贷系统需要每个月定时对用户账户扣款结算,会员系统需要定期检测用户剩余会员天数以便及时通知续费等等。Linux系统中内置的crontab一般被广泛地用于跑定时任务crontab指令解释命令行crontab -e进入crontab编辑,把自己要执行的指令编辑好之后保存退出即可生效。不过本文并不会过多讨论crontab的内容,而是要深入分析一下PHP Laravel框架是如何基于crontab封装出功能更加强大的任务调度(Task Scheduling)模块。对于定时任务,我们当然可以每个任务配置一个crontab指令。只不过这样做的话随着定时任务的增加,crontab指令也线性增长。毕竟crontab是一项系统级的配置,在业务中我们为了节约机器,往往对于量不大的多个项目会放在同一台服务器上,crontab指令多了就容易管理混乱,并且功能也不够灵活强大(无法随心所欲的停启、处理任务间依赖关系等)。对此Laravel的解决方案是只声明一条crontab,业务中的所有定时任务全都在这一条crontab中做处理和判断,实现在代码层面管理任务:即php artisan schedule:run每分钟跑一次(crontab的最高频率),至于业务上的具体任务配置,则注册于Kernel::schedule()中上述例子中我们可以很清晰的看到系统中注册了三项定时任务,并且提供了everyMinute, everyFifteenMinutes, daily, hourly等语义化的方法来配置任务周期。本质上,这些语义化的方法只是crontab表示方式的一个别称罢了,最终都会转化为crontab中的表达方式(如 * * * * * 表示每分钟执行一次)。如此一来,每分钟执行一次的php artisan schedule:run指令,会扫描Kernel::schedule中注册的所有指令并判断该指令配置的执行周期时候已经到期,如果到期则推入待执行队列。最后依次执行所有的指令。这里需要注意两个点,第一、如何判断指令是否已经Due了该执行了。第二、指令的执行顺序问题。首先,crontab表达式所指定的执行时间,是指绝对时间,而不是相对时间。所以仅仅根据当前时间和crontab表达式,即可判断出指令是否已经Due了该执行了。如果想要实现相对时间,那么必须存储上一次执行的时间,然后才能进行推算下次执行应该是什么时候。绝对时间和相对时间的区别可以用下面一幅图概括(crontab的执行时间如图中左侧列表所示)。Laravel中对于crontab表达式的静态分析和判断使用的是cron-expression库,原理也比较直观,就是静态的字符分析比对。crontab是绝对时间,而非相对时间第二个问题是执行顺序,前面的图中我们可以看出,如果你在Kernel::schedule方法中注册了多个任务,正常情况下它们是顺序依次执行的。也就是说必须要等到Task 1执行完成之后,Task 2才会开始执行。在这种情况下,如果Task 1非常耗时,则会影响到Task 2的按时执行,这一点在开发中是尤其需要注意的。不过在Kernel::schedule中注册任务时加上runInBackground即可实现任务的后台执行,这点我们下文详细讨论。前文提到的定时任务队列顺序执行的特性,前面的任务执行时间太长会妨碍后面任务的按时执行。为解决此问题,Laravel中提供了使任务后台执行的方法runInBackground。如:后台运行的原理,其实也非常简单。我们知道在linux系统下,命令行的指令最后加个“&”符号,可以使任务在后台执行。runInBackground方法内部原理其实就是让最后跑的指令后面加了“&”符号。不过在任务改为后台执行之后,又有了一个新的问题,即如何触发任务的后置钩子函数。因为后置钩子函数是需要在任务跑完之后立即执行,所以必须要有办法监测到后台运行的任务结束的一瞬间。我们从源代码中一探究竟$finished字符串的内容是一个隐藏的php artisan指令,即php artisan schedule:finish 该命令被附在了本来要执行的command命令后面,用来检测并执行后置钩子函数。php artisan schedule:finish 的源代码非常简单,用mutex_name来唯一标识一个待执行任务,通过比较系统中注册的所有任务的mutex_name,来确定需要执行哪个任务的后置函数。代码如下:有些定时任务指令需要执行很长时间,而laravel schedule任务最频繁可以做到1分钟跑一次。这也就意味着,如果任务本身跑了1分钟以上都没有结束,那么等到下一个1分钟到来的时候,又一个相同的任务跑起来了。这很可能是我们不想看到的结果。因此,有必要想一种机制,来避免任务在同一时刻的重复执行(prevent overlapping)。这种场景非常类似多进程或者多线程的免费云主机域名程序抢夺资源的情形,常见的预防方式就是给资源加锁。具体到laravel定时任务,那就是给任务加锁,只有拿到任务锁之后,才能够执行任务的具体内容。Laravel中提供了withoutOverlapping方法来让定时任务避免重复。具体锁的实现上,需要实现IlluminateConsoleSchedulingMutex.php接口中所定义的三个接口:该接口当然可以自己实现,Laravel也给了一套默认实现,即利用缓存作为存储锁的载体(可参考IlluminateConsoleSchedulingCacheMutex.php文件)。在每次跑任务之间,程序都会做出判断,是否需要防止重复,如果重复了,则不再跑任务代码:我们知道crontab任务最精细的粒度只能到分钟级别。那么如果我想实现30s执行一次的任务,需要如何实现?关于这个问题,stackoverflow上面也有一些讨论,有建议说在业务层面实现,自己写个sleep来实现,示例代码如下:如果runYourCode执行实现不太长的话,上面这个任务每隔1min执行一次,其实相当于runYourCode函数每30秒执行一次。如果runYourCode函数本身执行时间比较长,那这里的sleep 30秒会不那么精确。当然,也可以不使用Laravel的定时任务系统,改用专门的定时任务调度开源工具来实现每隔30秒执行一次的功能,在此推荐一个定时任务调度工具nomad。如果你确实要用Laravel自带的定时任务系统,并且又想实现更精确一些的每隔30秒执行一次任务的功能,那么可以结合laravel 的queue job来实现。如下:通过Laravel 队列功能的delay方法,可以将任务延时30s执行,因此如果每隔1min,我们都往队列中dispatch两个任务,其中一个延时30秒。另外,把自己要执行的代码runYourCode写在任务中,即可实现30秒执行一次的功能。不过这里需要注意的是,这种实现中scheduling的防止重合功能不再有效,需要自己在业务代码runYourCode中实现加锁防止重复的功能。到此,相信大家对“怎么理解Laravel定时任务调度机制”有了更深的了解,不妨来实际操作一番吧!这里是百云主机网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

相关推荐: php遍历数组的方法是什么

这篇文章免费云主机域名主要讲解了“php遍历数组的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“php遍历数组的方法是什么”吧! 两种方法:1、利用for循环,语法为“for($i=0;$i本文操作环…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 09/01 07:44
下一篇 09/01 07:44

相关推荐