WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 外部 API 呼び出しの非同期化)

外部APIの呼び出しなど時間がかかる処理で、投稿などの動作が遅くならないよう、WordPress に備わる WP-Cron という仕組みを利用して、実行を非同期化するテクニックを紹介します。 🙂


お天気API呼び出しの非同期化

前回は、アクションフックを用いて投稿時にお天気APIに問い合わせを行い、カスタムフィールドにお天気情報を格納する部分の実装を行いました。

主要部分の概念ソースコードは次のようになります。

class Otenki {
    function prepare($post_ID, $post) {
        // "今日"のポストの時だけ天気を取得判定
        // wp_remote_get 関数による http リクエスト
        // カスタムフィールドへの情報格納
    }
}

$otenki = new Otenki();
add_action(
    'publish_post', array($otenki, 'prepare'), 10, 2);

本処理を行った場合の、記事投稿時の処理シーケンスは以下のようになります。

otenki_seq3

今回注目するのは、上記黄色メモ部分「お天気APIの取得処理」と後続の処理の関係です。

APIの呼び出しには外的要因が多く、時間がかかったり予想外のデータが戻ってきたりする場合があります。自分の扱い知らぬところで動作するため、慎重な実装が必要です。

たとえば、ネットワークや対向 API サーバの都合で API 呼び出し処理に 60 秒かかってしまうとすると、後続がそれだけ遅れていきます。簡単なところでは投稿後、管理画面の再描画までにプラス 60 秒かかって可能性があるということです。これはちょっといただけません。

また、前回外部APIから取得した情報の処理にはしっかり正当性確認をしようという部分がありましたが、外的要因如何よっては PHP が Fatal Error で終了してしまうという不安もぬぐいきれません。 WordPress にはトランザクションロールバックの処理がありませんので、後続処理によってはデータが不整合してしまう可能性も出てきます。

もちろん、処理をなんとかひとつのトランザクションとして扱いたい場合はやむを得ないところなのですが、WordPress の場合主たる処理は記事の投稿です。今回のお天気のような付加情報は、また次回がんばろうという考え方ができます。

以上のような理由から、お天気APIの取得を記事投稿とは切り離した処理にしてしまおうというのが今回のお話です。このような実行の一部で、結果を待つこととなく処理を進めることを非同期処理と呼びます。

WP-Cron によるスケジュール

WordPress には決められた時間を元に、一定の処理を行う WP-Cron と呼ばれるスケジューラのが組み込まれています。たとえば、未来日に設定した投稿したが、その時間で公開されるのはこの機能の働きです。

また、WP-Cron で行う処理は、サイトや管理画面の表示とは別な流れで行われるようになっており、本筋の動作に影響がでません。これは本筋の処理から、バックグラウンドで動くスケジュール処理用の別な WordPress を起動する特殊な実装で動いているためで、次のように自分自身で WordPress に http リクエストを行い実行するようになっています。

/**
 * Send request to run cron through HTTP request that doesn't halt page loading.
 *
 * @since 2.1.0
 *
 * @return null Cron could not be spawned, because it is not needed to run.
 */
function spawn_cron( $gmt_time = 0 ) {

    /* 省略 */

    $cron_request = apply_filters( 'cron_request', array(
        'url' => site_url( 'wp-cron.php?doing_wp_cron=' . $doing_wp_cron ),
        'key' => $doing_wp_cron,
        'args' => array( 'timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters( 'https_local_ssl_verify', true ) )
    ) );

    wp_remote_post( $cron_request['url'], $cron_request['args'] );

}

このような WP-Cron の特徴を生かすと、外部 API 取得処理のような時間のかかる可能性がある処理を非同期化することができます。WordPress 本体内でも更新 ping の送信や Akismet のスパムチェックなどで同様のテクニックが使われています。

では実際に WP-Cron を使ってお天気 API 取得処理の非同期化を行ってみます。

API がよくできていますので、WordPress でスケジュールをつくるのは難しくありません。ここではひとつだけスケジュールを作成する wp_schedule_single_event 関数を使います。

wp_schedule_single_event 関数は引数で指定した時刻以降に、指定したアクションフックを呼び出してくれます。

アクションフックは任意のものが指定できますので、お天気 API の取得を行うフックを “extract_otenki” と命名し、その処理がかかれた execute メソッドを登録(add_action)してあげます。

前回つくった prepare の取得処理部分だけが execute メソッドに移り、代わりに wp_schedule_single_event でスケジュール登録する形になります。

最初の概念ソースコードとの差分は次のようになります。

publish_post アクションフックだけで処理していた最初のソース。

class Otenki {
    function prepare($post_ID, $post) {
        // "今日"のポストの時だけ天気を取得判定
        // wp_remote_get 関数による http リクエスト
        // カスタムフィールドへの情報格納
    }
}

$otenki = new Otenki();
add_action(
    'publish_post', array($otenki, 'prepare'), 10, 2);

wp_schedule_single_event で処理を非同期化したソース。

class Otenki {
    function prepare($post_ID, $post) {
        // "今日"のポストの時だけ天気を取得判定
        // 天気取得の非同期スケジュール作成
        wp_schedule_single_event(
            time()
            , 'extract_otenki'
            , array($post_ID));
    }

    function extract($post_ID) {
        // wp_remote_get 関数による http リクエスト
        // カスタムフィールドへの情報格納
    }
}

$otenki = new Otenki();

add_action('publish_post'
    , array($otenki, 'prepare'), 10, 2);
add_action('extract_otenki'
    , array ($otenki, 'extract'), 10);

prepare メソッドと extract メソッドで処理対象となる記事IDが、wp_schedule_single_event の引数を介して受け渡しできている部分にも注目してください。

ここまできてようやく、publish_post のアクションフックに登録している関数名が prepare(準備)になっていた謎が解明されたのでした。 🙂

まとめ

以上で、お天気 API 取得処理の非同期化ができました。

今回のポイントは、

  • 外部 API 呼び出しなど外的要因で速度の低下が見込まれる場合は非同期化しよう。
  • WP-Cron にスケジュールされた処理は、サイトの表示処理とは別な WordPress が起動して行われるので非同期化される。
  • wp_schedule_single_event は WP-Cron にスケジュールを行う関数で、実行時刻の登録とアクションフック名と、アクションフックへの引数を登録することが出来る

でした。

というわけで、また次回。 🙂


目次

本連載はファイルがない状態からソースコードを実装していく方式を採っています。プログラムの構成法も含めて解説していますので、最初から読んでいくとより理解しやすいでしょう。

アクションフックのプラグインをつくる編
最初に、WordPress の拡張において重要なフックの考え方を、サンプルの動作のひとつである「記事公開時の処理追加」の実装を元に解説します。フックはこの後の画面出力、処理の非同期化や管理画面の付与でも使われていきます。
処理のクラス化
WordPress 拡張におけるモジュール設計として、PHP のクラスを用いた方法を紹介します。分かりやすく、他のモジュールとの競合を最小限に抑えられる手法です。
外部 API の呼び出し
LWWS などの REST API に対して WordPress からリクエストを送る解説です。また、ユーザによって設定が異なる値がある場合のモジュール構成も合わせて紹介します。
外部 API 呼び出しの非同期化(この記事)
外的要因などで処理スピードの低下が懸念される場合の処理方法です。WP-Cron を用いたスケジュール化手法を解説しています。
テンプレートファイルからの情報出力
テンプレートファイルに処理を記述し出力する場合に、拡張モジュールと疎結合にする実装法を紹介します。アクションフックの活用例としても有効です。
管理画面をつける
管理画面に設定欄を設けユーザが拡張の機能をコントロールできるようにする手法の解説です。また、本連載中のソースコードに最終的なリファクタリングをかけ、他の開発者にこの拡張の意味を表現するプログラミングテクニックも解説します。
まとめ
本連載の概要とまとめ、そして目次です。

WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 外部 API 呼び出しの非同期化)」への1件のフィードバック

  1. ピンバック: wp_cron を使った外部データ取得の非同期化とそのデバッグ方法 | セルティスラボ

コメントを残す