【Google App Engine】30秒以上の処理を非同期通信で実現する方法

2015 年 9 月 23 日 水曜日

30秒ルール

Google App Engineでは、1回のリクエストに対し、30秒以内にレスポンスを返さなければいけない

上記の制約がある中で、バッチ処理のように、1回の処理に数分から数時間かかる処理を実現するにはどうすれば良いか?

多くの場合、答えはTaskQueueになると思いますが、TaskQueueは実行ユーザーがGoogle App Engine Adminになってしまいます。

処理を開始したユーザーで繰り返し実行したい場合—たとえば、あるユーザーにしか参照権限が無いSpreadSheetを、APIで検索する場合。Adminには参照権限が無いので、当然、接続に失敗します。

何度も実行する

30秒以内にレスポンスを返す処理を、非同期に、目的が達成するまで繰り返します。

クライアントサイド

index.jsp
————————

var query = {
  'state' : $("#state").val(),
  'startindex' : $("#startindex").val(),
};

$.ajax({
  url: "/do",
  type:"post",
  data: query,
  dataType: "TEXT",
  success: function(html){
    // レスポンス受信成功
    html = $.trim(html);
    document.getElementById('target_ajax_load').innerText = html;
    // レスポンス分解
    // レスポンスを連想配列にしておく
    var response = document.getElementById('target_ajax_load').innerText.split("\n");
    var keyvalue = new Array();
    for (var i = 0; i < response.length; i ++) {
      var kv = response[i].split("=");
      keyvalue[$.trim(kv[0])] = $.trim(kv[1]);
    }

    // 以下、レスポンステキストによって処理変更
    if (keyvalue['state'] == 'done'){
      // ★ 完了した場合
      var resulthtml = '完了しました';
      document.getElementById('target_ajax_load').innerHTML = resulthtml;
    } else if (keyvalue['state'] == 'step1' || keyvalue['state'] == 'step2' || keyvalue['state'] == 'step3'){
      // ★ タイムオーバーした場合(続きから検索)
      $('#state').val(keyvalue['state']);
      $('#startindex').val(keyvalue['startindex']);
      document.getElementById('target_ajax_load').innerHTML = "しばらくお待ちください..." + stateMessage;
      setTimeout("sync()", 5000);
    } else if (keyvalue['state'] == 'error'){
      // ★ エラー発生時(メッセージ出して終わり)
      document.getElementById('target_ajax_load').innerText = $.trim(keyvalue['error']);
    }
});

do.jsp
——————————

<%@page pageEncoding="UTF-8" isELIgnored="false" session="false"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>;
${response}
サーバサイドで処理を実行し、状態をレスポンスとして出力する。
index.jspでは、このレスポンスを見て、次に何をするかを決める。

サーバサイド

DoContoroller.java
——————————

int startindex = Integer.parseInt(request.getParameter("startindex"));
String state = request.getParameter("state");

if (state.equals("step1")){
    if (counter > hoge){
      // 処理が終わったら...
      response += "state=step2\n";  
      startindex = startindex + counter;
      response += "startindex=" + startindex + "\n";                
      requestScope("response", response);
      return forward("do.jsp");    
    } else {
      // 処理が終わらなかったら...
      response += "state=step1\n";  
      startindex = startindex + counter;
      response += "startindex=" + startindex + "\n";                
      requestScope("response", response);
      return forward("do.jsp");  
    }

} else if (state.equals("step2")){
 // 略
} else if (state.equals("step3")){
 // 略
}

処理失敗時のリトライであったり、
作業データのバックアップ、失敗時のリストアが必要かも。

関連記事

コメントをどうぞ

トラックバック

このエントリーのトラックバックURL:

http://www.bmoo.net/archives/2011/05/312388.html/trackback