Skip to content

【第 17 天】WP-Cron

Last updated on 2020 年 10 月 30 日

Cron 是基於時間的工作排程系統,而 WP-Cron 是 WordPress 中處理工作的基於時間的工作排程系。WordPress 的有很多的核心功能,例如說:檢查更新、排程發布文章這類的功能,都是使用了 WP-Cron。

WP-Cron 的工作原理是,在頁面被讀取的時候,才會開始檢查工作排程列表以查看需要運行的工作。 也因此 WP-Cron 和 Unix 系統的 Cron 運行方式是不一樣的。舉例:如果我們安排了一個工作在早上 10:00 運行,而在這之前都沒有發生任何頁面加載,則該排程也就都不會運行。

為什麼使用 WP-Cron

那為什麼我們要使用 WP-Cron 呢?

因為,很多虛擬主機不允許用戶訪問系統 Cron,但 WordPress 核心和許多外掛還是得需要一個 Cron 系統來執行一些排程工作呀!因此 WordPress 提供了這種通過頁面載入後去觸發 WP-Cron 系統的機制。

雖然這種運作方式,不一定會在特定的時間『準時』運行,但是 WP-Cron 也有一定的機率去完成我們的排程工作,所以還算是可以用。

想較於系統 Cron ,WP-Cron 系統也是有好處的,今天如果是 Unix 系統的工作排程,假設說沒有在特定的時間執行,他就永遠真的不會執行了。而 WP-Cron ,工作將一直在佇列中,直到有一個頁面加載時觸發他們。也就是說每一次頁面載入的時候,會去檢查並且執行過期而沒有運行的工作(不管這個工作過期過久),因此,通過 WP-Cron 設置的排程工作不會沒執行到。

理解 WordPress 工作排程

Unix 系統的 Cron 是會在特定的時間去運行,而 WP-Cron 則是使用時間間隔來模擬系統 Cron。

什麼意思?首先給第一個工作一個運行時間,然後設置一個時間間隔,以秒為單位,之後按照時間間隔重複工作。例如,我們安排了一個 早上 10:00 開始,間隔 300 秒 的排程工作。

會發生什麼事情?該工作首先會在這個 早上 10:00 被運行一次,然後在 早上 10:05 再被運行一次,然後每隔 300 秒就會被再運行一次。

創建一個時間間隔

WordPress 提供了 cron_schedules hook 讓我們可以創建一個過濾器來添加自定義時間間隔,如下:

add_filter( 'cron_schedules', 'example_add_cron_interval' );

function example_add_cron_interval( $schedules ) {
   $schedules[ 'every-5-minutes' ] = array(
       'interval' => 5 * MINUTE_IN_SECONDS,
       'display' => __( 'Every 5 minutes', 'devhub' ) );
    return $schedules;
}

我們就創建了一個新的時間間隔,允許我們每 5 分鐘運行一次 Cron 工作。

啟用 WP Cron 事件

排程週期性工作

為了讓我們的工作執行,我們需要創建一個自定義 hook,並掛載一個 callback function 到這個 hook 上,這是非常重要的一步,忘了這一步,我們的工作永遠不會被執行。

add_action( 'bl_cron_hook', 'bl_cron_exec' );

現在,我們來進行實際的工作排程。另外, WordPress 提供了一個 wp_next_scheduled() 函式來幫助我們檢查一個特定的工作是否已經被排程。

wp_next_scheduled() 函式很簡單,只接收一個參數 hook 的名稱,使用的時候如果有抓到工作排程,就會回傳一個包含下一個執行時間的字串,否則就會回傳 false ,使用方法如下:

wp_next_scheduled( 'bl_cron_hook' );

重複工作

我們使用 wp_schedule_event() 來完成重複工作的呼叫,該函式需要三個必需的參數,參數如下:

  1. $timestamp:首次執行此工作的 Unix timestamp
  2. $recurrence:執行工作的重複時間間隔的名稱
  3. $hook:我們自定義 hook 名稱

我們將使用 5 秒的時間間隔和我們之前創建的 hook 來演示一下:

wp_schedule_event( time(), 'five_seconds', 'bl_cron_hook' );

記住,我們需要首先確保該工作排程尚未被安排,程式碼如下:

if ( ! wp_next_scheduled( 'bl_cron_hook' ) ) {
   wp_schedule_event( time(), 'five_seconds', 'bl_cron_hook' );
}

取消工作排程

當我們不再需要某個排程工作時,我們也可以取消,我們使用 wp_unschedule_event() 函式來取消工作排程,該函式有兩個參數:

  1. $timestamp:下一次工作的 timestamp
  2. $hook:我們自定義 hook 名稱

這個函式不僅可以取消在每個 timestamp 執行的排程工作,還可以取消未來所有的工作排程。

得到下一個工作的 timestamp

如果不知道下一個工作的 timestamp,我們可以使用 wp_next_scheduled() 函式來查找,該函式需要一個參數:

  1. $hook:我們自定義的 hook 名稱

把上面的程式碼組合在一起使用,看起來應該像下面這樣:

$timestamp = wp_next_scheduled( 'bl_cron_hook' );
wp_unschedule_event( $timestamp, 'bl_cron_hook' );

當我們不再需要某些工作排程的時候,取消這些工作排程非常重要,不然 WordPress 會繼續嘗試執行他們。

取消排程工作的重要時機就是禁用外掛的時候,很多外掛都忘記了這一點,忘記清理不再需要的工作排程。 而 WordPress 也為我們提供了一個名為 register_deactivation_hook() 函式,允許開發人員在外掛停用的時候使用,詳細可以參考我第六天分享的內容:WordPress 啟用、停用、刪除外掛

程式碼如下:
PHP

register_deactivation_hook( __FILE__, 'bl_deactivate' );

function bl_deactivate() {
   $timestamp = wp_next_scheduled( 'bl_cron_hook' );
   wp_unschedule_event( $timestamp, 'bl_cron_hook' );
}

掛載 WP-Cron 到系統 Corn

如我們上面介紹的,WP-Cron 的執行依賴於頁面加載,不一定會嚴格『準時』執行。但是如果我們真的有那種必需『準時』執行的工作,這會是一個問題。

還好,我們有一個簡單的解決辦法來避免這個問題。我們只需將系統的工作排程設置為按我們所需的時間間隔執行即可。什麼意思?

就是我們直接使用工具向 wp-cron.php 文件發送請求。

關掉 WP-Corn

在這之前,因為我們會直接在系統上安排工作排程,也就不需要 WordPress 在每個頁面載入的時候,幫我們執行工作排程,所以我們可以先禁用掉這個功能,來節約這個不必要的伺服器負擔。

我們可以在 wp-config.php 中設定開關,在 wp-config.php 中加上以下程式碼即可。

define('DISABLE_WP_CRON', true);

設定系統工作排程

Mac OS X 和 Linux

因為我手邊沒有 Windows ,所以這邊我就只有介紹 Mac OS X 和 Linux 。用 Windows 的朋友可能要另外爬文了。Mac OS X 和 Linux 的部分都是使用 Cron 作為基於時間的排程系統,可以直接從 terminal 設定。

crontab -e

Cron 有一個特定的語法。

https://i0.wp.com/ithelp.ithome.com.tw/upload/images/20200917/20121194ht7Y6g7FDK.png?ssl=1

設置工作排程的時候,如果不考慮的時間段,我們會使用 * 號代替。例如,如果我們需要每隔 15 分鐘運行一次命令,而不考慮小時、日期和月份,排程工作設置如下:

15 * * * * command

許多伺服器都自帶了 wget 工具,我們可以直接使用這個工具來訪問 WordPress 的 Cron 文件。

wget http://YOUR_SITE_URL/wp-cron.php

搭配上剛剛學到的 corn ,假設我們每天凌晨觸發網站的 WordPress Cron ,最後的 Cron 設置會變成這樣:

0 0 * * * wget http://YOUR_SITE_URL/wp-cron.php
Published in2020鐵人賽WordPress

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

error: 尊重智慧財產權,請不要隨意複製使用文章喔