Engineer in Tokyo

Google App Engine SDK 1.4.0 がリリースされました!

Google App Engine1.4.0がリリースされました!!このリリースはかなりでかい!!チャンネルAPI,”Always On”(リザーブインスタンス)、タスクキューの正式リリース、スタートアップリクエスト、バックグラウンド処理の改善などなど、

チャンネルAPI

まずは、一番重要なチャンネルAPI。チャンネルAPIでクライアントブラウザーにプッシュすることができるようになります。内部的には、Google TalkXMPPインフラを使っているらしくて、それでスケールアウトしてくれます。チャンネルAPIは2つの部分がに分けています。サーバー再度のチャンネルAPIとチャンネルAPIのJavaScriptライブラリ。

チャンネルAPIはサーバー側から、クライアントの通信に使います。クライアントからサーバーへの通信は今までどおりのPOSTがGET HTTPリクエスト。

サーバー側

まずは、チャンネルのIDをクライアントに渡す。クライアントはそのIDを使って、チャンネルに接続する。

channel.create_channel()に渡すuserは、ユーザーだけじゃなくて、内部的に文字列にするので、create_channel()に渡すデータは単の文字列でも大丈夫です。

from google.appengine.ext import webapp
from google.appengine.api import channel
from django.template.loader import render_to_string

class MyHandler(BaseHandler):
    def get(self):
        user = users.get_current_user()

        # ユーザーのチャンネルを作る
        # create_channel に渡すデータは単の文字列でも大丈夫
        id = channel.create_channel(user)

        return self.response.out.write(
            render_to_string(
                "index.html",
                {"channel_id": id},
            )
        )

クライアント側のJavaScriptはチャンネルに接続

var channel = new goog.appengine.Channel("{{ channel_id }}");
var socket = channel.open();
socket.onopen = function () {
    window.setTimeout(function () {
        alert("Connected!");
    }, 100);
};

// メッセージのハンドラーを登録
socket.onmessage = function (evt) {
    // テキストを受けているけど、JSONがおすすめ
    // var o = JSON.parse(evt.data);
    alert(evt.data);
    // do something
};

サーバーからクライアントにメッセージを送る

from google.appengine.api import channel
from google.appengine.api import users

class AjaxHandler(BaseHandler):
    def get(self):
        user = users.get_current_user()

        # メッセージをクライアントに渡す。
        # クライアントが接続している状態が不要
        # 誰も接続してない場合は何もしない
        # ここでテキストデータを送るけど、JSONがおすすめです
        channel.send_message(user, "Hello World!!")

Channel API のドキュメント: http://code.google.com/intl/ja/appengine/docs/python/channel/overview.html

Always On

今までは、リクエストが少ない場合は、App Engineのサーバーインスタンスがすべて落とされるので、リクエストがその状態で来た時、かなりスピンアップ(サーバーインスタンスの起動)に時間かかってしまいました。”Always On”という機能を使うと3つのインスタンスを保持してくれます。有料ですが、かなりスピンアップに困っている人に好調的だね。

Note: “Always On”はアクセスがあんまりこない時のみに効果があるので、自分のアプリは常にトラフィック量が高い場合、最低3つのインスタンスを保持してもしょうがないです。3つまで下がってないからです。

スタートアップリクエスト

この機能もスピンアップにいいのですが、効果がちょっと違うので、説明します。今まで、リクエストが増えて、スケールアウト(新しいインスタンスの起動)が必要な場合、あるリクエストが新しいインスタンスに割り当てると、インスタンスがロードに時間かかったり、DeadlineExceededErrorが出たりしました。

スタートアップリクエスト機能は、スケールアウトが必要な場合、ユーザーからのリクエストを割り当てる前に、スタートアップリクエストをインスタンスに投げる。このリクエストで必要なモジュールを未然にロードできるようになります。それで、最初のユーザーリクエストが来たら、より速く返せるようになります。

つまり、スタートアップリクエストがスケールアウトする時に効果があるので、インスタンスがいくらでもあっても効果的です。

使うには、まずはメールみたいに、inbound_servicesapp.yamlに設定します。

inbound_services:
    - warmup

スタートアップリクエストが/_ah/warmupのURLに来るので、スタートアップリクエストを受けとるURLをapp.yamlに設定する。

- url: /_ah/warmup.*
  script: warmup.py

warmup.pyの中に、必要そうなモジュールをインポートする。

# ロードが重いモジュールを未然にロードする
import mybigmodule
import myothermodule

def main():
    print "Content-type: text/plain"
    print "OK"

タスクキュー正式リリース

今まで、App EngineのタスクキューはBetaでgoogle.appengine.api.labs.taskqueueに入っていたけど、labsから卒業するので、google.appengine.api.taskqueueに移動される。タスクキューのデータもデータのクオータに含まれるようになります。データクオータにひっかかるので、ヘビーに使っている人たちはちょっと大変かもしれない。

cron/タスクキューの改善

cronとタスクキューの時間制限は今まで、30秒でしたが、10分になります。ですが、長く実行しているcronジョブ・タスクは認識され、別インフラに移動されて、スループット(実行頻度)が落ちるので、速くて小さいタスクが良好だと言われる。

Metadata クエリー

App Engineデータストアのメタデータ、Namespace,Kind,Propertyをクエリすることができるようになりました。メタデータのモデルクラスはgoogle.appengine.ext.db.metadataモジュールに入っています。

Kindインスタンスが持っているPropertyの親になるので、あるKindPropertyを取得するには、ancestor()クエリができます。

from google.appengine.ext.db.metadata import Namespace, Kind, Property

for namespace in Namespace.all():
    print namespace.namespace_name

for kind in Kind.all():
    print kind.kind_name
    for property in Property.all().ancestor(kind):
        print "    %s" % property.property_name

ダウンロード

ダウンロードページではまだ出てないみたいですが、code.google.comのプロジェクトの以下のURLからダウンロードできます。