13.CA Beatのシステム基盤 第一回「GAE/Jで使うフレームワーク」 | CA Beat エンジニアのブログ

CA Beat エンジニアのブログ

Google App Engineをメインに技術情報を発信しています。

$CA Beat エンジニアのブログ-13.gae_default


CA Beat エンジニアリーダーのヤマサキ(@vierjp)です。


前回まではAppEngineの比較的新しめの機能をメインにブログを書いてきましたが、
今回からはCA Beatのシステム基盤に関する事を中心に書いていこうと思います。


○CA Beatで使用しているフレームワーク

CA Beatでは、私が入社して Google App Engine/J のアプリを社内で開発し始めた時から、
サーバーサイドは全てSlim3を使って開発しています。

GAE/J登場当初から情報を追っていた人には
「AppEngineにおけるフレームワークの向き不向き」については今さらな話題かと思いますが、
以前CA Beatで運用していた「外部の会社に開発を委託したシステム」
某DIコンテナを使っていた事で苦労した事もあり、
これからAppEngine上で動作するアプリを開発しようとしている人に向けて書いておきたいと思います。



○AppEngineの特性を意識していないフレームワークを使った場合

AppEngine登場以前から非クラウド環境(オンプレミス)では
「Spring Framework」や「Seasar2」といったDIコンテナが多く使われてきました。

これらを使って開発した事のある方には経験があるかと思いますが、
ある程度の規模になればアプリの起動に数十秒~1分程度かかることもあります。

非クラウド環境においては
「起動時に全ての初期化処理を行い、以降は初期化済みの状態でユーザーのリクエストを素早く処理する」
というアプローチは昔から普通に行われてきましたし、
基本的に「一度アプリを起動したらサーバーは起動しっぱなし」なので、
この起動時間の長さが問題になることは少ないです。

しかし、「スピンアップ」・「スピンダウン」という特性のあるAppEngineにおいては、
起動時間の長さは大きな欠点となってしまいます。




○AppEngineの「スピンアップ」・「スピンダウン」とは

AppEngineではユーザーからのリクエストがあった場合、
・既に起動中のインスタンスで処理できるかを判定
・難しければ(起動中のインスタンスが空くまでに時間がかかりそうなら)新しいインスタンスを起動する
という形で「トラフィックの増加に応じた自動スケールアウト」を実現しています。

この起動処理が「スピンアップ」で、
逆に「しばらく使っていないインスタンスを自動的に停止する」のが「スピンダウン」です。

この仕組みは
・「負荷が高い場合には自動でインスタンスが追加されるためトラフィックの増加に自動で対応できる」
・「負荷が少ない場合には自動でインスタンスが停止され無駄なCPUコストがかからない」
という点で良いのですが、
その代わりに、ユーザーは自身のリクエストによってスピンアップが発生した場合に、
レスポンスが返るまでに「スピンアップにかかる時間分」通常より余計に待たされる事になります。

よってスピンアップが遅いフレームワークを使った場合には
このタイミングでユーザーが待たされる時間が長くなります。

アプリの起動に20秒かかるとした場合、
ユーザーは「20秒 + そのリクエストを処理するための時間」待たされてしまいます。

この「スピンアップが発生した際にユーザーが数十秒待たされる」という時点でかなり問題なのですが、
AppEngineはユーザーのリクエストに対しては、
1分以内(以前は30秒以内)にレスポンスを返さなければエラーになる仕様です。

そのため、起動にかかる時間によっては
「制限時間に間に合わずインスタンスが起動しない」というさらに悪い事態すら起こり得ます。

最初からAppEngine用として作られた「Slim3」は、
この「スピンアップ」・「スピンダウン」というAppEngineの特性を意識して作られており、
リクエストを処理するための必要最低限の初期化処理だけを行う事で、
スピンアップに要する時間を短くしています。


* SDK1.4.0から追加された「Warmup Request」で
 スピンアップにかかる時間の問題は緩和されているのかもしれませんが、
 それでも起動に時間がかかるフレームワークを使った場合には長時間待たされる事が多々ありました。
 また、明示的に「Idle Instances」を設定することでインスタンスを起動したままにもできますが、
 こちらはその分コスト(CPU利用時間に対する課金額)がかかりやすくなってしまうため、
 「負荷が少ない時には無駄なCPUコストがかからない」という点が失われ、
 自動スケールアウトの魅力が半減してしまいます。



○Low Level APIを使ってDatastoreにアクセスする

AppEngineではDatastoreにアクセスするための公式のAPIとして、以下の3つが用意されています。
・Java Data Objects(JDO)
・Java Persistence API(JPA)
・Low Level API

GAE/J登場当初、ほとんどの人は高レベルなAPIであるJDOやJPAから試していたと思います。
実際私が初めて買ったGoogle App Engineの本も、JDOを使った解説をメインにしていました。

しかし、JDO・JPAと比べた起動時間の速さを大きな理由として、
徐々に「Low Level APIを使うのが良い」という流れにシフトしていったと記憶しています。

また、JDOやJPAを使う場合、
「API的にデータアクセス処理がAppEngineに依存しないのでAppEngine以外の環境にリプレイスしやすい」というメリットが一見ありそうですが、
実際のところ、API的にAppEngineに依存しなくてもRDB向けの設計とDatastore向けの設計は異なるので、
多くの場合にそのまま移行するという流れにできるかは少々疑問です。
(ごく小さいシステムならそのまま移行できるかもしれませんが)

Slim3のデータアクセス部分の処理は、
「Low Level API」をベースにより開発をしやすくするためのラッパーとして作られています。



○Slim3の特徴

・高速なスピンアップ
 前述の通りです。

・HOT reloading
 Slim3にはSeasar2同様のHot Reloading機能が付いています。
 これは「ソースコードを変更した場合でもアプリケーションの再起動無しに変更が反映される」機能です。
 このおかげで「まるでスクリプト言語のように」動かしながらサクサク開発することができます。
 他のフレームワークでも「JRebel」等を使えば同様の事ができますが、
 Slim3ならこの機能を標準で、無料で使えます。
 
・タイプセーフなクエリ
 S2JDBCの「流れるようなインターフェース」と似た書き方ですが、さらに
 カラム名(プロパティ名)と条件の指定をタイプセーフに行うことができます。


CarMeta meta = CarMeta.get();
List list = Datastore.query(meta)
.filter(meta.year.greaterThan(1999))
.sort(meta.year.desc)
.asQueryResultList();


ポイントは、Entityのプロパティ名がMetaクラスに定数として定義されていてそれを指定できる事。
これにより、
1.プロパティ名を正しく指定していない場合にコンパイルエラーになるため、
 プロパティ名の変更や記述ミスによる指定の間違いを防ぐことができる
2.eclipseのコード補完を使ってサクサク書ける

というメリットがあります。

1は「リファクタリング性の向上」と「ミスの減少」
2は「開発効率の向上」
に繋がるでしょう。

この「Metaクラス」はModelクラスからaptで自動生成されるため、作成の手間もありません。


* Metaクラスはクエリの条件指定だけでなく、
Slim3内部で「Low Level APIのEntity型」とModelクラスの相互変換等にも使っていて、
「aptで処理を自動生成することによってリフレクションの使用を減らして速度低下を防ぐ」という目的もありそうです。


他にも海外製の「Low Level API」をラップする形のAppEngine用データアクセスフレームワークとして
「Objectify」というフレームワークもあります。
これまた「外部の会社に開発を委託したシステム」が使っていたので少し触ったのですが、
以下のようにfilter条件、sort条件を文字列で指定するあたり、
Slim3のデータアクセスの方が記述が楽な上に安心だと感じました。

List cars = ofy().load().type(Car.class)
.filter("year >", 1999)
.order("-year")
.list();

Queries - objectify-appengine - Queries and Indexes


Slim3はデータアクセスの処理に加えシンプルなMVCタイプのWebフレームワークとしての機能もあります。
Webフレームワーク部分はとてもシンプルなので、
Strutsの利用経験だけでもあれば、そう苦労せず扱うことができるでしょう。
JSP周りの実装方法もシンプルです。


参考:Slim3 日本語サイト(非公式)



○まとめ

今回の話は"GAE/J 上で利用するフレームワークとしての向き不向き"の話で、
Slim3以外のフレームワークそれ自体の良し悪しの話ではありません。
私自身「非クラウド環境」では長いことDIコンテナを使ってきましたし、
自宅サーバーでは6年前からずっとSeasar2で作ったシステムが動いています。

それでもGAE/JにおいてはやはりSlim3が一番と感じています。
特に「職場でエンジニアを集めて開発をする」という前提であれば、
AppEngineやAppEngine用フレームワークの経験があるエンジニアをアサインする事は難しいので
日本語の情報を得やすく、詳しい書籍もあって学習しやすいという点でも良いと思います。


ブログの第1回記事でも紹介させていただきましたが、
AppEngineのデータストア・アクセスの学習にはやはりこの本が良いと思います。
今回書いたSlim3のメリットについても冒頭で丁寧に書かれていますので、
Slim3の使用を検討する際にも読んでみると良いでしょう。

CA Beatに参加していただいた方には会社で購入して無償貸与致しますw


オープンソース徹底活用 Slim3 on Google App Engine for Java/ひが やすを

¥2,730
Amazon.co.jp


追記:
最後に、先日 ja night でお話させていただく機会があった際にもお伝えさせていただきましたが、
ひがさん、素晴らしいフレームワークを公開してくださりありがとうございます。
Seasar2時代からずっとお世話になってます。m(`・ω・´)m