ひがやすを技術ブログ

電通国際情報サービスのプログラマ

AppEngine/Jのspin-upを劇的に改善する方法

AppEngineは、アクセスがあったときにアプリケーションを起動し、しばらくアクセスが無ければアプリケーションを終了させ、また次のリクエストで再起動するという仕組みを導入しています。
そのため、アプリケーションを起動(spin-up)する時間がとても重要になってきます。このspin-upの時間はpython(webapp)で60cpu_ms以下。(cpu_msはcpuが使う仮想的な時間ですがmsと同じ感じで捉えてもらってもとりあえずは大丈夫です)JavaServletだと600cpu_msくらいです。PythonでもDjangoような大きなフレームワークだと1000cpu_msくらい(アプリによる)かかりますが、許容範囲内。JavaだとSlim3で1300cpu_ms、Springだと早くて7000cpu_msという感じで、Slim3がギリギリ許容範囲内でしょうか。ほんとうは、1000cpu_msは切りたいところです。


AppEngine for Javaはspin-upとの戦いだと言っても言い過ぎではありません。しかし、それも過去の話になりました。
サーバーサイドテンプレートエンジン(JSPとか)を使うのをやめ、最初にブラウザにはHTMLのモックを返し、HTMLの中で動的に変更する部分だけ、Ajaxを使ってサーバーから引っ張ってくるのです。
これを仮に、HTML centric pull architecture with Ajaxと呼んでおきます。既に知られた呼び方があれば即効で変えるので誰か指摘してください。


例えば、最初にブラウザは次のようなindex.htmlにアクセスします。

<body>
<span id="ready"></span><br />
<span id="message"></span><br />
<input type="button" value="hello"
    onclick="$('#message').load('hello');"/>
    
<script src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
$(function() {
    $('#ready').load('ready');
});
</script>
</body>

scriptタグをbodyの最後に持ってきているのは、JavaScriptのロードで描画を中断しないようにするためです。これで(Mockとなる)HTMLが最初に描画され、ユーザは直ぐに画面を見ることができます。
動的に変える部分はreadyイベントやclickイベントで書き換えています。


readyとhelloのURLに対応するSlim3のControllerは次のようになります。

public class ReadyController extends Controller {

    @Override
    public Navigation run() throws Exception {
        response.getWriter().write("Ready");
        return null;
    }
}
public class HelloController extends Controller {

    @Override
    public Navigation run() throws Exception {
        response.getWriter().write("Hello");
        return null;
    }
}


Viewでサーバーからデータを引っ張ってくるので、pull architectureと呼んでいます。pullという言い方は確かWebWorkが使っていた気がします。


AppEngineでは、HTMLをstaticファイルとして登録しておくと、App Serverまで処理がわたらず手前の専用のstaticファイルサーバーで処理されるので高速です。(JSPだとstaticファイルとして登録できない)
実は、App Serverに処理がわたらないとspin-upも起きないので、最初のHTMLの描画はspin-upなしで行われます。これがちょっとしたトリック。


実際のspin-upはリクエストがReadyControllerに渡ったときに起きますが、JSPを使っていないので、Jasperの初期化(5,600cpu_ms)を避けることができ、しかもHTMLの情報を含んでいない純粋に必要なデータのみをやり取りしているので、spin-up時間は非常に短いものになります。


AppEngineのログによるとindex.htmlは0cps_ms、ReadyControllerはspin-upした時でも630cpu_ms(ほぼ素のServletと同様)くらいで処理出来ています。自分が試した感じでは、spin-upした時としてない時の差は実感することができません。ほぼ同じ感じ。これは、すぐに描画が終わり、非同期でサーバーにアクセスしているせいだと思います。


次のURLで実際に試すことが出来ます。
http://test.latest.higayasuo.appspot.com/ajax/index.html


HTMLが描画されてからReadyの文字が出るまで若干間がありますが、HTMLは既に描画済みなためほとんど気にならないでしょう。


これで完全にAppEngine for Javaに勝った気がするv(o^_^o)v ぶいっ

ただ、この方法は、自分がすべて考えたわけではなく、従来からあったのをまとめただけです。素のHTML使ってAjax使うやり方は、appengine ja nightで小川さんがやっていたと思います。後、ガラケーでは基本使えません。