GAE/Jをcron代わりに使う

  • 投稿 : 2011-08-03
  • 更新 : 2013-05-12

方針

Google App Engineはcronという仕組みがあるので、これを使用してcronの使えないレンタルサーバーでのcron代わりに使います。java(GAE/J)を使います。

イメージとしてはこのような感じです。
GAEのクローンで定期的に、Cron.javaを呼び出します。Cron.javaの中で外部のレンタルサーバーのcron.phpを呼び出します。30秒以内で処理を終えたほうがよさそうなので、cron.phpは軽い処理にしておくほうがよいでしょう。30秒以内に終わらないときは、GAEの仕様でエラーになります。

cron.phpの中で処理を書かずに、非同期でtask.phpを呼び出してtask.php中でやりたい処理を書くような構造にします。このようにすれば、Cron.javaの応答時価何は30秒以内に収まると思います。

このように3層構造にしなくても大丈夫な気もしますが、GAE側をほとんど触りたくないのでこのようにしておくとあとあとカスタマイズはしやすいと思います。

GAEのデプロイほかに関しては、この記事下の「ブログ内で関連ある記事」を参考にしてください。関連ある記事とこの記事で必ずできると思います。

GAE/J側の処理(Cron.java)

■Cron.java
package k05;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.io.BufferedReader;
import java.util.Scanner;
import javax.servlet.http.*;

//SuppressWarnings("serial")
public class Cron extends HttpServlet {
    //private static final long serialVersionUID = 1L;

    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {

        String ret="ok";
        String uri="http://レンタルサーバー/cron.php";
        try {
             URL url = new URL(uri);
             BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
                 String line;
                 while ((line = reader.readLine()) != null) {
                    // ...
                 }
            reader.close();
        } catch (MalformedURLException e) {
            ret="ng";
                // ...
        } catch (IOException e) {
                  ret="ng";
                // ...
        }
        resp.setContentType("text/plain");
        resp.getWriter().println(ret);
    }

}
このプログラムが定期的に呼び出されると、その内部で
http://レンタルサーバー/cron.php
が呼び出されるので、結果として
http://レンタルサーバー/task.php
が定期的に呼び出されてるかのようにふるまう

■warWEB-INFweb.xml
以下を追加
<servlet>
    <servlet-name>Cron</servlet-name>
    <servlet-class>k05.Cron</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Cron</servlet-name>
    <url-pattern>/cron</url-pattern>
</servlet-mapping>

上記の設定で、Cron.javaが
http://アプリケーションID.appspot.com/cron
に関連づけられる

■war\WEB-INF\cron.xml
<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/cron</url>
    <description>Check for update</description>
    <schedule>every 2 minutes</schedule>
  </cron>
</cronentries>

この設定で、http://アプリケーションID.appspot.com/cron
が2分毎に呼び出されることになる。

Cronをクリア(取り消し)したい場合は、
■warWEB-INFcron.xml
<?xml version="1.0" encoding="UTF-8"?>
<cronentries/>

レンタルサーバ側の処理(cron.php,task.php)

■cron.php

内部で以下のURLを呼び出すことを前提としたソース
http://レンタルサーバー/cron/task.php
<?php
$host = "レンタルサーバー";  //書き換えてください
$path = "/cron/task.php";

async_request($host,$path);

function async_request($domain,$path,$port=80) {
    $fp = fsockopen($domain, $port, $errno, $errstr, 5);
    if ( !$fp ) {
        return false;
    } else {
        $out = "GET " . $path . " HTTP/1.0\r\n";
        $out .= "Host: ".$domain."\r\n";
        $out .= "Connection: Close\r\n\r\n";
        fwrite($fp, $out);
        fclose($fp);
    }
    return true;
}
?>
■task.php
ここに本来やりたい処理を書きます。
今回は動作確認のため、呼び出されたらログをファイルに書くものを作って確認しました。
<?php
log_write("cron");

function log_write($data){
    $time = time() + 9*3600;  //GMTとの時差9時間を足す
    $time2= gmdate("Y/m/d H:i:s ", $time);

    $fp = fopen("log.txt", "a");
    fwrite($fp,$time2.":".$data."\n");
    fclose($fp);
}
?>
スポンサーリンク