トップ / システム / cron 自動実行

cron 自動実行

このページでできること

  • cron からクヌギスキーマの自動クロールを走らせる仕組み(cron/run_cron.php)の動作を把握できます。
  • 「1 時間に 1 回 cron を叩き、毎回 N 件ずつ消化する」新仕様の流れを理解できます。
  • 1 万 URL 規模のプロダクトでも、1 日に複数回の cron 起動で確実に消化できる設計と、その安全策(トークン保護・URL レベル重複防止)を理解できます。
  • cron が動かないときの切り分けポイントがわかります。

エントリポイント: cron/run_cron.php。Web 画面ではなく、サーバの cron や CLI から呼び出します。1 時間に 1 回叩く前提で組まれています。

新仕様の考え方

クヌギスキーマの cron は 「日に何度叩いても、その日にまだ取っていない URL だけを上限件数まで処理する」 設計になっています。これにより、URL 数の多いプロダクトでも 1 回あたりの cron を有限時間で終わらせ、残りは次の毎時起動に持ち越すことができます。

1 万 URL のプロダクトの動作イメージ
  • スケジュール: 例えば 1 日と 15 日にチェック、間隔 5 秒、上限 500 URL/cron。
  • cron は毎時 0 * * * * で起動。
  • 1 日 0:00 に最初の cron が走ると、未クロール 500 URL を処理 → 残り 9,500。
  • 以降 1:00、2:00 ... と毎時 500 URL ずつ処理し、約 20 時間で 1 万 URL を消化。
  • 翌 1:00 以降の cron は「当日未クロール 0 件」と判定してスキップ。

1 起動の流れ

cron/run_cron.php が起動されると、次の処理が走ります。

  1. CLI 経由なら無条件で先に進む。Web 経由の場合は data/cron_secret.txt が存在し、かつ ?secret=<値> が一致しないと 403 forbidden で停止。
  2. set_time_limit(0)ignore_user_abort(true) でタイムアウト抑制。
  3. 本日の日付(YYYY-MM-DD)と日(j= 1〜31)を取得。
  4. Repository::getCronMaxUrlsPerRun() で「この cron 実行で処理する URL 数の上限」を取得(Cron スケジュール 画面で設定。既定 500、0 で無制限)。 $remaining 変数として保持。
  5. Repository::allSchedulesDueToday() で「enabled=1 + 今日の日が days_json に含まれる」プロダクトを product_id ASC 順に抽出(last_triggered_date による日次ロックは撤廃済み)。
  6. 該当プロダクトを product_id の小さい順に処理:
    1. $remaining が 0 以下になったら「URL 予算を使い切ったので残りプロダクトはスキップ」して終了。
    2. Repository::listUrlsNotCrawledOnDate($pid, $today) で「当日まだクロール結果が無い URL」だけを取得(id ASC 順)。
    3. 0 件なら「pending なし」としてスキップ(ただし last_triggered_date は更新)。
    4. 1 件以上なら、残り予算分だけ array_slice() で切り出して Crawler->run($pid, $batch, $delay, 'cron')
    5. 消化した件数を $remaining から引く。
    6. cron_log へ 1 行 INSERT、cron_schedule.last_triggered_date を本日に更新。
  7. 最後に「cron finished: processed=N url(s)」を出力して終了。
「同じ URL が同日に二度走らない」のはなぜ? 旧仕様では cron_schedule.last_triggered_date によって「1 日 1 プロダクト 1 回」をブロックしていました。新仕様では last_triggered_date によるブロックは外し、代わりに URL 1 件ずつcrawl_results に当日(JST)の行があるか?」を直接チェックする方式に変わりました。これにより、毎時 cron でも重複は起きません。同時に last_triggered_date「最後に cron がクロールを動かした日」を示す情報として引き続き更新されます。

CLI からの呼び出し(推奨)

# 毎時 0 分に実行(1 時間に 1 回)
0 * * * * /usr/bin/php /<配置先絶対パス>/cron/run_cron.php >> /<配置先絶対パス>/data/cron.log 2>&1

Web 経由での呼び出し(トークン保護必須)

cron デーモンが使えない環境では、外部の死活監視サービスやUptimeRobot のような HTTP 監視ツールから定期的に叩く構成も可能です。ただし必ずトークン保護を有効にしてください。

  1. サーバ上で十分長いランダム文字列を生成: openssl rand -hex 32
  2. その文字列を data/cron_secret.txt に 1 行で保存(末尾改行はあってもなくても OK)。
  3. 外部監視から https://<サーバ>/cron/run_cron.php?secret=<その文字列> を 1 時間に 1 回叩く。
  4. data/cron_secret.txt が存在しない、あるいは ?secret= が一致しない場合は 403 を返します。Basic 認証の認可情報を URL に含められない外部監視サービスでも、この方式なら使えます。
Web 経由での実行に関する注意
  • 外部監視からの呼び出し URL(とくに ?secret=)は、HTTPS のクエリ文字列としてプロキシのログに残る可能性があります。可能な限り CLI 経由を選んでください。
  • Basic 認証を有効にしている場合、 cron/run_cron.php も Basic 認証の対象になります。CLI からの実行には影響しませんが、Web 経由で叩く場合は監視ツール側に Basic 認証の資格情報も渡す必要があります。
  • 共用サーバの PHP 実行時間制限により、上限を大きくしすぎると HTTP タイムアウトに当たる可能性があります。Cron スケジュール の「1 回あたりの URL 上限」を控えめにし、その代わり cron 頻度(毎時、30 分ごと等)を上げて分散させてください。

cron_log での実行履歴の見方

cron が動いた痕跡は次の 3 か所に残ります。

うまく動かないときのチェックポイント

症状対処
cron 時刻になっても何も起きない data/cron.log にエントリが追記されているか確認。何も無ければ cron 設定そのものが動いていない(パス・PHP のフルパス・cron デーモン稼働を見直す)。
「no products due today」とログに出る 本日の日(1〜31)が、どのプロダクトの cron_schedule.days_json にも含まれていない、または enabled=0 状態。Cron スケジュール 画面で見直す。
毎時走っているのに進まない cron ログに「pending=0」が並んでいないか確認。当日分は既に消化済みかもしれません(翌実行日まで何も起きないのが正常)。再評価したい場合は 手動でクロール実行 から「全 URL を再クロール」を実行。
「cron URL budget exhausted」とログに出る 1 回の上限まで処理した正常終了です。次の毎時起動で続きを処理します。Cron スケジュール の「1 回あたりの URL 上限」を増やすか、cron 頻度を上げることで消化スピードを調整できます。
HTTP 経由で叩いて 403 data/cron_secret.txt が存在し、内容が ?secret= と一致しているか確認。改行の有無で比較に失敗することがあるので、テキストエディタで末尾改行を整える。
関連ページ 実行日と「1 回あたりの URL 上限」の設定は Cron スケジュール、エックスサーバーでの具体的な設定例は インストール方法 → Xserver での Cron 設定例。手動で残りを消化したい場合は 手動でクロール実行