欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

怎么理解Laravel定時(shí)任務(wù)調(diào)度機(jī)制

本篇內(nèi)容主要講解“怎么理解Laravel定時(shí)任務(wù)調(diào)度機(jī)制”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“怎么理解Laravel定時(shí)任務(wù)調(diào)度機(jī)制”吧!

成都創(chuàng)新互聯(lián)是網(wǎng)站建設(shè)技術(shù)企業(yè),為成都企業(yè)提供專(zhuān)業(yè)的成都網(wǎng)站建設(shè)、成都網(wǎng)站制作,網(wǎng)站設(shè)計(jì),網(wǎng)站制作,網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制適合企業(yè)的網(wǎng)站。十余年品質(zhì),值得信賴(lài)!

1. 基本實(shí)現(xiàn)邏輯

一個(gè)復(fù)雜的web系統(tǒng)后臺(tái)當(dāng)中,一定會(huì)有很多定時(shí)腳本或者任務(wù)要跑。

例如爬蟲(chóng)系統(tǒng)需要定期去爬取一些網(wǎng)站數(shù)據(jù),自動(dòng)還貸系統(tǒng)需要每個(gè)月定時(shí)對(duì)用戶(hù)賬戶(hù)扣款結(jié)算,

會(huì)員系統(tǒng)需要定期檢測(cè)用戶(hù)剩余會(huì)員天數(shù)以便及時(shí)通知續(xù)費(fèi)等等。Linux系統(tǒng)中內(nèi)置的crontab一般被廣泛地用于跑定時(shí)任務(wù)

crontab指令解釋

命令行crontab -e進(jìn)入crontab編輯,把自己要執(zhí)行的指令編輯好之后保存退出即可生效。

不過(guò)本文并不會(huì)過(guò)多討論crontab的內(nèi)容,而是要深入分析一下PHP Laravel框架是如何基于crontab封裝出功能更加強(qiáng)大的任務(wù)調(diào)度(Task Scheduling)模塊。

對(duì)于定時(shí)任務(wù),我們當(dāng)然可以每個(gè)任務(wù)配置一個(gè)crontab指令。只不過(guò)這樣做的話(huà)隨著定時(shí)任務(wù)的增加,crontab指令也線(xiàn)性增長(zhǎng)。

畢竟crontab是一項(xiàng)系統(tǒng)級(jí)的配置,在業(yè)務(wù)中我們?yōu)榱斯?jié)約機(jī)器,往往對(duì)于量不大的多個(gè)項(xiàng)目會(huì)放在同一臺(tái)服務(wù)器上,c

rontab指令多了就容易管理混亂,并且功能也不夠靈活強(qiáng)大(無(wú)法隨心所欲的停啟、處理任務(wù)間依賴(lài)關(guān)系等)。

對(duì)此Laravel的解決方案是只聲明一條crontab,業(yè)務(wù)中的所有定時(shí)任務(wù)全都在這一條crontab中做處理和判斷,實(shí)現(xiàn)在代碼層面管理任務(wù):

* * * * * php artisan schedule:run >> /dev/null 2>&1

即php artisan schedule:run每分鐘跑一次(crontab的最高頻率),至于業(yè)務(wù)上的具體任務(wù)配置,則注冊(cè)于Kernel::schedule()中

class Kernel extends ConsoleKernel
{
    Protected function schedule(Schedule $schedule)
    {
        $schedule->command('account:check')->everyMinute(); // 每分鐘執(zhí)行一次php artisan account:check 指令
        $schedule->exec('node /home/username/index.js')->everyFifteenMinutes(); //每15分鐘執(zhí)行一次node /home/username/index.js 命令
        $schedule->job(new MyJob())->cron('1 2 3 10 *'); // 每年的10月3日凌晨2點(diǎn)1分向任務(wù)隊(duì)列分發(fā)一個(gè)MyJob任務(wù)
    }
}

上述例子中我們可以很清晰的看到系統(tǒng)中注冊(cè)了三項(xiàng)定時(shí)任務(wù),并且提供了everyMinute, everyFifteenMinutes, daily, hourly等語(yǔ)義化的方法來(lái)配置任務(wù)周期。

本質(zhì)上,這些語(yǔ)義化的方法只是crontab表示方式的一個(gè)別稱(chēng)罷了,最終都會(huì)轉(zhuǎn)化為crontab中的表達(dá)方式(如 * * * * * 表示每分鐘執(zhí)行一次)。

如此一來(lái),每分鐘執(zhí)行一次的php artisan schedule:run指令,會(huì)掃描Kernel::schedule中注冊(cè)的所有指令并判斷該指令配置的執(zhí)行周期時(shí)候已經(jīng)到期,

如果到期則推入待執(zhí)行隊(duì)列。最后依次執(zhí)行所有的指令。

// ScheduleRunCommand::handle函數(shù)
public function handle()
{
    foreach ($this->schedule->dueEvents() as $event) {
        if (! $event->filtersPass()) {
            continue;
        }
        $event->run();
    }
}

這里需要注意兩個(gè)點(diǎn),第一、如何判斷指令是否已經(jīng)Due了該執(zhí)行了。第二、指令的執(zhí)行順序問(wèn)題。

首先,crontab表達(dá)式所指定的執(zhí)行時(shí)間,是指絕對(duì)時(shí)間,而不是相對(duì)時(shí)間。所以?xún)H僅根據(jù)當(dāng)前時(shí)間和crontab表達(dá)式,

即可判斷出指令是否已經(jīng)Due了該執(zhí)行了。如果想要實(shí)現(xiàn)相對(duì)時(shí)間,那么必須存儲(chǔ)上一次執(zhí)行的時(shí)間,

然后才能進(jìn)行推算下次執(zhí)行應(yīng)該是什么時(shí)候。絕對(duì)時(shí)間和相對(duì)時(shí)間的區(qū)別可以用下面一幅圖概括(crontab的執(zhí)行時(shí)間如圖中左側(cè)列表所示)。

Laravel中對(duì)于crontab表達(dá)式的靜態(tài)分析和判斷使用的是cron-expression庫(kù),原理也比較直觀,就是靜態(tài)的字符分析比對(duì)。

crontab是絕對(duì)時(shí)間,而非相對(duì)時(shí)間

第二個(gè)問(wèn)題是執(zhí)行順序,前面的圖中我們可以看出,如果你在Kernel::schedule方法中注冊(cè)了多個(gè)任務(wù),

正常情況下它們是順序依次執(zhí)行的。也就是說(shuō)必須要等到Task 1執(zhí)行完成之后,Task 2才會(huì)開(kāi)始執(zhí)行。

在這種情況下,如果Task 1非常耗時(shí),則會(huì)影響到Task 2的按時(shí)執(zhí)行,這一點(diǎn)在開(kāi)發(fā)中是尤其需要注意的。

不過(guò)在Kernel::schedule中注冊(cè)任務(wù)時(shí)加上runInBackground即可實(shí)現(xiàn)任務(wù)的后臺(tái)執(zhí)行,這點(diǎn)我們下文詳細(xì)討論。

2. 后臺(tái)運(yùn)行

前文提到的定時(shí)任務(wù)隊(duì)列順序執(zhí)行的特性,前面的任務(wù)執(zhí)行時(shí)間太長(zhǎng)會(huì)妨礙后面任務(wù)的按時(shí)執(zhí)行。

為解決此問(wèn)題,Laravel中提供了使任務(wù)后臺(tái)執(zhí)行的方法runInBackground。如:

// Kernel.php
protected function schedule(Schedule $schedule)
{
    $schedule->command('test:hello') // 執(zhí)行command命令:php artisan test:hello
    ->cron('10 11 1 * *') // 每月1日的11:10:00執(zhí)行該命令
    ->timezone('Asia/Shanghai') // 設(shè)置時(shí)區(qū)
    ->before(function(){/*do something*/}) // 前置hook,命令執(zhí)行前執(zhí)行此回調(diào)
    ->after(function(){/*do something*/}) // 后置鉤子,命令執(zhí)行完之后執(zhí)行此回調(diào)
    ->runInBackground(); // 后臺(tái)運(yùn)行本命令
    // 每分鐘執(zhí)行command命令:php artisan test:world
    $schedule->command('test:world')->everyMinute();
}

后臺(tái)運(yùn)行的原理,其實(shí)也非常簡(jiǎn)單。我們知道在linux系統(tǒng)下,命令行的指令最后加個(gè)“&”符號(hào),可以使任務(wù)在后臺(tái)執(zhí)行。

runInBackground方法內(nèi)部原理其實(shí)就是讓最后跑的指令后面加了“&”符號(hào)。不過(guò)在任務(wù)改為后臺(tái)執(zhí)行之后,

又有了一個(gè)新的問(wèn)題,即如何觸發(fā)任務(wù)的后置鉤子函數(shù)。因?yàn)楹笾勉^子函數(shù)是需要在任務(wù)跑完之后立即執(zhí)行,

所以必須要有辦法監(jiān)測(cè)到后臺(tái)運(yùn)行的任務(wù)結(jié)束的一瞬間。我們從源代碼中一探究竟

// 構(gòu)建運(yùn)行在后臺(tái)的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字符串的內(nèi)容是一個(gè)隱藏的php artisan指令,即php artisan schedule:finish <mutex_name>。

該命令被附在了本來(lái)要執(zhí)行的command命令后面,用來(lái)檢測(cè)并執(zhí)行后置鉤子函數(shù)。

php artisan schedule:finish <mutex_name>的源代碼非常簡(jiǎn)單,用mutex_name來(lái)唯一標(biāo)識(shí)一個(gè)待執(zhí)行任務(wù),

通過(guò)比較系統(tǒng)中注冊(cè)的所有任務(wù)的mutex_name,來(lái)確定需要執(zhí)行哪個(gè)任務(wù)的后置函數(shù)。代碼如下:

// 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. 防止重復(fù)

有些定時(shí)任務(wù)指令需要執(zhí)行很長(zhǎng)時(shí)間,而laravel schedule任務(wù)最頻繁可以做到1分鐘跑一次。

這也就意味著,如果任務(wù)本身跑了1分鐘以上都沒(méi)有結(jié)束,那么等到下一個(gè)1分鐘到來(lái)的時(shí)候,又一個(gè)相同的任務(wù)跑起來(lái)了。

這很可能是我們不想看到的結(jié)果。因此,有必要想一種機(jī)制,來(lái)避免任務(wù)在同一時(shí)刻的重復(fù)執(zhí)行(prevent overlapping)。

這種場(chǎng)景非常類(lèi)似多進(jìn)程或者多線(xiàn)程的程序搶奪資源的情形,常見(jiàn)的預(yù)防方式就是給資源加鎖。

具體到laravel定時(shí)任務(wù),那就是給任務(wù)加鎖,只有拿到任務(wù)鎖之后,才能夠執(zhí)行任務(wù)的具體內(nèi)容。

Laravel中提供了withoutOverlapping方法來(lái)讓定時(shí)任務(wù)避免重復(fù)。具體鎖的實(shí)現(xiàn)上,需要實(shí)現(xiàn)Illuminate\Console\Scheduling\Mutex.php接口中所定義的三個(gè)接口:

interface Mutex
{
    // 實(shí)現(xiàn)創(chuàng)建鎖接口
    public function create(Event $event);
    // 實(shí)現(xiàn)判斷鎖是否存在的接口
    public function exists(Event $event);
    // 實(shí)現(xiàn)解除鎖的接口
    public function forget(Event $event);
}

該接口當(dāng)然可以自己實(shí)現(xiàn),Laravel也給了一套默認(rèn)實(shí)現(xiàn),即利用緩存作為存儲(chǔ)鎖的載體(可參考Illuminate\Console\Scheduling\CacheMutex.php文件)。

在每次跑任務(wù)之間,程序都會(huì)做出判斷,是否需要防止重復(fù),如果重復(fù)了,則不再跑任務(wù)代碼:

// Illuminate\Console\Scheduling\Event.php
public function run()
{
    // 判斷是否需要防止重復(fù),若需要防重復(fù),并且創(chuàng)建鎖不成功,則說(shuō)明已經(jīng)有任務(wù)在跑了,這時(shí)直接退出,不再執(zhí)行具體任務(wù)
    if ($this->withoutOverlapping && ! $this->mutex->create($this)) {
        return;
    }
    $this->runInBackground?$this->runCommandInBackground($container):$this->runCommandInForeground($container);
}

4. 如何實(shí)現(xiàn)30秒任務(wù)?

我們知道crontab任務(wù)最精細(xì)的粒度只能到分鐘級(jí)別。那么如果我想實(shí)現(xiàn)30s執(zhí)行一次的任務(wù),

需要如何實(shí)現(xiàn)?關(guān)于這個(gè)問(wèn)題,stackoverflow上面也有一些討論,有建議說(shuō)在業(yè)務(wù)層面實(shí)現(xiàn),自己寫(xiě)個(gè)sleep來(lái)實(shí)現(xiàn),示例代碼如下:

public function handle()
{
    runYourCode(); // 跑業(yè)務(wù)代碼
    sleep(30); // 睡30秒
    runYourCode(); // 再跑一次業(yè)務(wù)代碼
}

如果runYourCode執(zhí)行實(shí)現(xiàn)不太長(zhǎng)的話(huà),上面這個(gè)任務(wù)每隔1min執(zhí)行一次,其實(shí)相當(dāng)于runYourCode函數(shù)每30秒執(zhí)行一次。

如果runYourCode函數(shù)本身執(zhí)行時(shí)間比較長(zhǎng),那這里的sleep 30秒會(huì)不那么精確。

當(dāng)然,也可以不使用Laravel的定時(shí)任務(wù)系統(tǒng),改用專(zhuān)門(mén)的定時(shí)任務(wù)調(diào)度開(kāi)源工具來(lái)實(shí)現(xiàn)每隔30秒執(zhí)行一次的功能,

在此推薦一個(gè)定時(shí)任務(wù)調(diào)度工具nomad。

如果你確實(shí)要用Laravel自帶的定時(shí)任務(wù)系統(tǒng),并且又想實(shí)現(xiàn)更精確一些的每隔30秒執(zhí)行一次任務(wù)的功能,那么可以結(jié)合laravel 的queue job來(lái)實(shí)現(xiàn)。如下:

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();
    }
}

通過(guò)Laravel 隊(duì)列功能的delay方法,可以將任務(wù)延時(shí)30s執(zhí)行,因此如果每隔1min,我們都往隊(duì)列中dispatch兩個(gè)任務(wù),其中一個(gè)延時(shí)30秒。

另外,把自己要執(zhí)行的代碼runYourCode寫(xiě)在任務(wù)中,即可實(shí)現(xiàn)30秒執(zhí)行一次的功能。不過(guò)這里需要注意的是,這種實(shí)現(xiàn)中scheduling的防止重合功能不再有效,

需要自己在業(yè)務(wù)代碼runYourCode中實(shí)現(xiàn)加鎖防止重復(fù)的功能。

到此,相信大家對(duì)“怎么理解Laravel定時(shí)任務(wù)調(diào)度機(jī)制”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!

網(wǎng)頁(yè)名稱(chēng):怎么理解Laravel定時(shí)任務(wù)調(diào)度機(jī)制
文章位置:http://aaarwkj.com/article0/gpijoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名網(wǎng)站維護(hù)、外貿(mào)建站品牌網(wǎng)站制作、標(biāo)簽優(yōu)化虛擬主機(jī)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

微信小程序開(kāi)發(fā)
91人妻精品丰满少妇区| 国产一区二区毛多内射| 一区二区三区av天堂| 激情亚洲综合一区二区| 中文字幕av一区二区人妻| 中文字幕久久熟女蜜桃| 99精品热这里只有精品| 青青草原一区二区三区| 日本在线一区二区视频麻豆| 九九视频免费观看5| 国产视频传媒一区二区| 日产极品一区二区三区| 国内成人免费在线视频| 老女人性生交大片免费| 欧美大片免费在线播放| 亚洲午夜一区二区精品| 日本一区二区 视频| 欧美成人日本在线播放| 深夜三级福利在线观看| 91精品麻豆国产自产在线| 日本欧美自拍偷拍高清| 精品妇女一区二区三区| 81精品国产综合久久精品伦理| 欧美黄片视频在线免费看| 国产精品专区日产一区| 小黄片免费在线播放观看| 欧美亚洲午夜一二综合| 成人久久精品一区二区| 久久精品国产av一一区| 欧美日韩一区二区综合在线视频| 国产亚洲黄片免费在线观看| 色男人天堂网在线视频| 韩国黄色理论片一区二区麻豆| 日本一区二区三级在线观看| 极品大胸美女被啪啪的高潮| 日韩av一区二区国产| 18禁视频免费无遮挡| 日本在线免费观看91| 国产成人精品久久性色av| 亚洲精品国产熟女久久| 日本a级片免费在线观看|