2014年6月4日水曜日

【GAE】初級実装編 初めに

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、クラウド基盤「Google App Engine(以下、GAE)」の連載しています。

調査はここまで

今までGAEの連載の中で、一先ずGAEがどんな特性を有するモノであるのか、という事が概ね見えてきました。

しかしですね、すでに13回も連載していますが、GAEというのは非常に広大なテーマにつき、まだ実装関連については全く記載していません。

この調子で書き進めて行ったのでは10年経っても終わらないんじゃないか、という気がしてきました。

そこで、今までの13回の連載は「事前調査編」という扱いにして、ここから先はもう実装に入ってしまおうかと思います。

実装しながら随時、その時使っている技術をご紹介していきます。

やること

さて、実装編と言っても、何を作るかがポイントですよね。

実は、我が社には「商品在庫入出庫システム」という新人研修用の基本的なスキルアップカリキュラムがあるのです。
本来はピュアなJavaで作るカリキュラムですが、これと同じようなモノをGAEで作って行こうと思います。

ポイントは「同じようなモノ」であって、「同じモノ」では無いということです。

これは過去の連載でも何度も記述していますが、GAEには個性があるのです。

「今あるシステムを同じ仕様で移植すれば良いよね♪」

という発想は愚の骨頂
100%、無駄に苦労しただけで不便なシステムが出来上がります。

GAEで作る以上は、GAE最適化を行わなければなりません。

「単純なJavaシステムをGAEで作るとこうなっちゃうんだ」というbeforeAfterがこの連載の目玉になります。

機能詳細

具体的には以下のような処理の実装方法の紹介になります。

  • ログイン
  • データベースへの検索、登録、更新、削除
  • 複数テーブルのJOIN
  • トランザクションの実装
  • JUnitでテスト

まあ、普通のJavaで作る話であれば、特に語ることはありませんよね。
Javaの経験者であれば、どのようなロジックにてこれら機能が実現するかは暗黙の了解で通るレベルの基本的機能です。

しかし、GAEで作るとなると、そうは行きません。

例えば、GAEでは基本的にjspは使用しないのがセオリーです。


「商品検索結果一覧をjspを使わないで作れ」


なんて言われたら、もうそれは基本スキルではありません。
答えはAjaxを使用するわけですが、ならば「Ajaxライブラリはどれにする?」という話になりますよね?

ちょっとの機能でもどんどん話が膨らんでいってしまうのです。


GAE開発は大変なのです。


しかし一方で、「一通りのスキルセットを習得すれば、後は案外すんなり行くものだ」というのが私の所感です。

ここは一つ、自分も新人になった気分で、『GAEという新しい言語を覚える!!』という意気込みで臨んでいきたいと思います。

終わりに

次回よりプロジェクト開始です。

導入するライブラリを一通り揃える所から始まります。

2014年5月30日金曜日

【GAE】システム構成図案

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、クラウド基盤「Google App Engine(以下、GAE)」の連載しています。

本質を知ろう

さて、今までの記事で巨大なGAEの一部を検証して参りました。

それについて見えてきたのは、「GAEには普通のシステムとは異なる個性がある」ということです。

この個性とは、GAEの本質と言い換えることも出来ます。

ここで一つ、大事な事を明言しておきましょう。


  • IT技術は、その本質を理解している者でなければ使用出来ない。


よくあるんですよ。



XXという技術が便利で凄いという噂を聞いた。⇒ザックリ調査⇒使えるみたいだし、早速使おう!!⇒爆死



GAEに限らず、IT技術には「限られた局面でのみ力を発揮する」という局地戦特化の性質を持つものがあるのです。


そのIT技術を使用するに相応しい要件、シチュエーション。向き不向き。それが本質です。

本質を理解しないままIT技術を導入しますと、高確率で要件とのミスマッチが発生して、もしくはその技術の真価を発揮させることが出来なくて、最終的には爆死することになります。

特にGAEの場合、その個性がかなり強い方である上に、基幹技術ですからね。

ちょっとライブラリを導入するとか、そんなレベルではなく、ここの本質を見誤ると爆死も爆死。
物理的にゴールに辿り着けなくなる恐れさえあります。

ここから更にGAEについて掘り下げていく前に、この辺りで一回、本質について整理してみましょう。

振り返り+少々

スピンアップ

GAEの強烈な個性の一つは「スピンアップ」です。

ここ数回の記事で長々とスピンアップについて検証してきましたが、

  • GAEは使用していない時間帯にインスタンスが落ちる。
  • インスタンスが起動するタイミングで数秒~10秒くらい待たされる。
  • この為、フルAjaxを使用せざるを得ない。(jspには不向き)

この辺りがGAEの特徴の一つ「画面インターフェースの制約」です。

DBが特殊

DBについてはまだ記事にしていませんが、内々に検証しておりますので、フライングで少々記載します。

Google App Engineは、DBが普通のリレーショナルデータベースではなくて、Bigtableという特殊なDBなのです。

これに伴い、普通のDBなら出来て当然のことが出来ません。

例えば、count(*)とかは出来ません。
やるなら全レコードを検索して、Javaで行数をカウントすることになりますが、そんなことやってたら超遅いですよね?
なので、レコード件数をカウントする機能が重要な要件には向きません。

他にはjoinが無いとか。

joinを実現したい場合は、Javaで複数回クエリを投げてマッピングを取るなど、コーディング実装のレベルで作り込む必要があります。

つまり、複雑なクエリが必要になる機能は実現出来ません。
GAEはシンプルな要件にしか適応出来ないのです。

安定性、高可用性

一方で、大量データの取り扱いなど、安定性、高可用性には無類の強さを発揮します。
何せ、GmailなどGoogleのアプリは全部コレで捌いているわけですから。

「アクセス殺到によりダウン」ということはまず無いと言い切ってしまって良いかと。

障害は偶に発生しているような未確認情報も入りますが、それでも自社でサーバを持つよりはよっぽど安定していると思います。

サーバ自体の信頼性はかなり高度な域にあると考えて良いです。


システム構成図

他にも色々とGAEには特徴がありますが、その中でも「本質」と呼べるような特記事項中の特記事項は以上の辺りかと思います。

この辺りを考慮に入れて、私はGAEの力をフルに発揮するシステム構成図案を考えてみました。

それが以下、「GAEによるWebAPI戦略」です。



端末はスマートフォンも視野

最近はAndroidやiPhoneなどの、スマートフォン、タブレットの普及が著しいですよね。
しかし、あれらは携帯端末というハードウェアの性能の都合上、重たい処理を端末側に持たせることが出来ません。

この為、重たい処理や大量データの確保が必要な場合は別途サーバを立てて、そこからHTTP通信によりXMLやJSONの形式でデータを取得する手段が多く取られます。

元々、GAEサービスのフルAjax化しなければなりませんので、サーバ本体は必ずWebAPIとしての役割を担うことになります。

このついでに、PCだけでなくスマホアプリまで視野に入れるというのは一石二鳥で合理的なのです。

圧倒的に頑強な本体

WebAPIアプリの性質上、中枢である本体サーバがダウンしてしまっては話になりません。

そこで、GAEの圧倒的な頑強さが力を発揮するのです。
GAEを本体とすることは、自社のサーバを持つよりも圧倒的に安心です。

また、上記のスマホアプリ計画と連動しますが、アプリに人気が出てアクセスが殺到することも考慮したいケースもありますよね?

自社でサーバを持っていた場合は人気が出始めてからサーバを増強しなければいけませんが、
GAEは自動的にスケールアップしていきますので、そのような配慮は無用!!

将来の大人気アプリ、大人気サービスを目指すからこそ、GAEの力が真価を発揮出来るのです。

機能間の独立

これは技術的というより、体制的、政治的な配慮になるかもしれません。

ハッキリ言って、「GAEに精通していて、Ajaxも出来て、AndroidアプリもAppleアプリも開発出来るエンジニア」なんていませんよ。

普通のWebシステム開発って、一人の人間がHTMLを書いて、Javaも書いて、SQLも書いて、一連の機能を実現しますよね?
でも、GAE開発はそんなの絶対無理です。


  • Ajaxは書けるけど、GAEは知らない。
  • Androidは知ってるけど、GAEは知らない。
  • GAEは覚えたけど、他のスキルまで習得している余裕が無い。


こんな調子になって、開発メンバーを招集出来ないに決まっています。
しかし、このWebAPI体制を取ることで、「GAE本体担当」「HTML担当」「Android担当」「Apple担当」とスキル毎に明確に独立することが出来るのです。

その技術単品だけ分かっていればOK!!

これなら技術者も集められます。

機能制約の確保

上記に繋がりますが、GAEには実現出来ない要件というのもあります。

何が出来て何が出来ないのかはそのプロジェクト毎に精査しなければなりませんが、確実に言えることは一つ。

「WebAPIインターフェースで提供していない機能は、実現出来ないという意味だ!!」
「WebAPIインターフェースで提供している機能の範囲内でアプリを実現するべき!!」

という、ポリシーの確保が必要だと言うことです。

これも「GAE本体担当」という専任者を置くことで、かなりの精度でポリシーを守り抜くことが出来るはずなのです。

終わりに

以上が、私の考えているGAEのシステム構想です。

我ながら合理的な作りになっているのではないかと思います。

しかし残念なことに、「Ajax」「Android」「Apple」、これら全部を開発するようなビッグな案件が私の身近に無いのですよね。。。

まあ、将来的な目標として温めておくとしまして、しばらくはAjaxベースで検証を進めていきたいと思います。


次回以降は、GAEの個性的なデータベース「ビッグテーブル」について連載していきたいと思います。

2014年5月19日月曜日

【追加】データベースのテスト支援ツール DbUnit その5 リプレース機能編

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、私の現場ではこのブログを参考資料としてJUnit開発を進行中です。

現場で使っているうちに、もっと便利な機能も見つかってきましたので、その補強連載中です。

固定じゃ対応出来ないことも

さて、この連載のテーマである「DbUnit」は、DBのレコードと位置づける定義XML「データセット」を前もって用意しておきます。

その「データセット」の内容をDBに初期登録して環境をセットアップする。
もしくは、検索結果がその「データセット」と同じである事を以て、正常を確認する。

そういう使い方なわけです。

この「データセット」は、基本は固定なのです。
固定値を「データセット」として定義しておくというのが基本的な使い方です。

しかし、場合によっては「固定値」では都合が悪いことも。。。

例えば、「本日から7日以内に作成されたレコードを検索する」のテストを行いたい場合、
もちろん「テストを実行する本日から7日以内」のレコードをセットアップしなければなりません。

でも、「本日」の定義は毎日変わっていってしまいますよね?
だから、投入するデータセットは固定では対応出来ません。
「本日から7日以内」に相当する時間部分のカラムだけは動的にセットする必要がある。

こういう需要なわけです。

他の需要を言いますと、「テスト毎にちょっとしか定義に差が無いのに、毎回似たようなデータセットを作るのが面倒!!」というケースでしょうか?

これは気持ちは理解できますが、積極的にはオススメ出来ないですね。
データセットを動的にしてしまいますと、テストケース自体にバグが潜在してしまうリスクが発生してしまいますから。

余りにデータセットが増えすぎてゴジャゴジャになってしまうという場合は一考するのもありですが、基本は固定データセットで行うべきかと思います。

ReplacementDataset

では本題の動的対応の解説です。

この機能は、DbUnitでは「ReplacementDataset」というクラスで実現出来ます。

「Replace」という名前の通り、この機能は置換です。


前もってダミーの値をデータセットにセットしておいて、後で置換する。


こういう戦術なわけです。

まず、普通のデータセットを見てみましょう。

<dataset>
   <shohin id="111" shohin_name="テスト商品" create_at="2013-01-20" />
</dataset>

これをセットアップするJavaソースはこちら。

// データセットの取得
IDataSet dataset = new FlatXmlDataSetBuilder().build(new FileInputStream("src/test/resource/jp/co/net/genesis/entity/商品_登録数3.xml"));
// セットアップ実行
DatabaseOperation.CLEAN_INSERT.execute(connection, dataset);

この基本セットアップ機能自体の解説については、過去の連載をご確認下さい。

さて、今回は、この「create_at」の時間が固定では困る、という話です。
ここを何でもいいのでダミーに差し替えます。

<dataset>
   <shohin id="111" shohin_name="テスト商品" create_at="[-7day]" />
</dataset>

[-7day]の部分がダミー値です。

「文字列置換」ですから、特にコーディング規約はありません。分かればOKです。
ただ、分かり易いように、必ず本日にしたいカラムは[TODAY]、必ず一週間前にしたいカラムは[-7day]といった自主ルールを作っておくと
可読性が高まって便利かと思います。

そして、これをセットアップするJavaソースはこちら。

// データセットの取得
IDataSet dataset = new FlatXmlDataSetBuilder().build(new FileInputStream("src/test/resource/jp/co/net/genesis/entity/商品_登録数3.xml"));
// リプレース
ReplacementDataSet expectedDataSet = new ReplacementDataSet(dataset);
expectedDataSet.addReplacementObject("[-7day]","2014-05-11" );
// セットアップ実行
DatabaseOperation.CLEAN_INSERT.execute(connection, expectedDataSet);

以下の部分が置換実行の箇所です。


  • expectedDataSet.addReplacementObject("[-7day]","2014-05-11" );

まあ、何てことは無い、普通の置換ですね。
このサンプルでは分かり易いように「"2014-05-11"」と固定値で置換していますが、
実際の現場では、この日付文字列を動的にセットするようにロジックを組めばいいわけです。

くれぐれも「日付文字列を動的にセットするロジックがバグっていた」というオチが無いようにお気をつけを。


ちなみに、上のサンプルは「セットアップレコードの置換」の内容になっていますが、
「SQLで登録結果を確認する為のデータセットの置換」も全く同じやり方でOKです。

テスト対象のSQLでレコードを作成して、そのレコードの「作成日付が本日日付になっていることの確認」みたいな時に使うと良いでしょう。

終わりに

ひとまず、現場で新しく上がったJUnit関連の要望はこの辺りでフォロー完了です。

また何か新情報を見つけたら追加記載します。

2014年5月8日木曜日

【追加】最強モックツール JMockit その13 DI(Spring)対応

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、私の現場ではこのブログを参考資料としてJUnit開発を進行中です。

その現場にて必要になった新情報を補強連載しております。

DI

今回の記事で重要になるキーワード、それは「DI」です。
依存性の注入(Dependency injection)という意味でして、Javaのクラス宣言を普通に「new ○○」とやるのではなく、外部XML等に宣言して
クラス間の結合を疎にするというソフトウェアパターンです。

このDIの機能を提供してくれるフレームワークをDIコンテナと言います。

DIコンテナの代表格と言えば、やっぱり「Spring」ではないでしょうか?

老舗にして大御所。
Javaを使っている人は、いつかどこかでお世話になるフレームワークかと思います。

DIの利便性

DIを使うことの利点、それは「クラス間が疎結合になる」ことです。

と言いましても、ピンと来ない人も多いかもしれませんね。


「クラス間が疎結合になったから何だって言うんだ?」


全く普通の感想かと思います。

DIコンテナを使うと、クラスを生成する際に普通に「new ○○」とは書けなくなって、
XMLファイルに色々宣言したりと手間が掛かるようになります。

私もいくつかの現場でSpringを使った事がありますが、振り返って考えてみると、
「クラス間が疎結合になり、ただ面倒になっただけ」という失敗ソースが多かったですね。

Springは結構難しいフレームワークなので、深く理解してからクラス設計しないと大失敗に陥ってしまいます。

DIコンテナについて記述していくと一回の記事で終わりませんので手短に行かせて貰いますが、
DIコンテナの利便性の一つ、それは「スタブが使い易い」ということです。

DIによるスタプ

まずはDIを使った簡単なサンプルを以下に記述します。

public class Sample01 {
 
 /** DIコンテナで自動セットするDAO */
 @Autowired
 private SampleDao sampleDao;
 
 public void test() {
  
  //DB検索実行
  sampleDao.findAll();
  
 }

}

このソース、「new SampleDao()」なんてやってる箇所が無いですよね?
SampleDaoはDIによってクラス生成時に自動セットされますので、自分でnewする必要は無いのです。
「@Autowired」のアサーションが、自動セットするフィールドであることを示すマークです。

これは分かっている人にとってはソースがスッキリして良いのですが、分からない人にとっては混乱するだけという、ちょっとハードルのある機能です。

さて、この「SampleDao」は、実はインターフェースでして、実クラスではありません。
実クラスが何なのかはソース中ではなく外部クラスに定義されています。


DIの特徴:ソース中に出てくるのはインターフェースだけ。実クラスは出てこない。


では、実クラスはどうやってセットされるのか?
それを外部のXMLファイルに記述するわけです。

<bean id="target" class="jp.co.net.genesis.sample.Sample01Impl" / >

こんな感じです。

インターフェースの中身は外部定義ファイルに定義されます。
これがDIコンテナの最大の特徴。


  • 本番時は本番用の外部定義ファイルを読み込む。⇒本物のクラスを定義
  • テスト時はテスト用の外部定義ファイルを読み込む。⇒テスト用スタブを定義


こうすることで、ソースの中身に手を加えることなく本番モードとテストモードを切り替えることが出来るわけです。

この機能は大規模開発で役に立つことが多いですね。
複数のチームで一つのシステムを作るような大規模開発の場合、一つのチームの進捗遅れや手違いで全チームが影響を受けることもしばしば。

しかし、DIコンテナを使うことで、
「本番用ソースは別チームが開発中」、「こっちは先にスタブでテストを済ませておく」みたいな戦術が使えるようになり、
チーム毎やクラス毎の独立性が確保されるようになるわけです。

DIのモック化

しかしですね、上記の場合、「わざわざ本番用とスタブの2コのクラスを作らなければならない」という手間もあるわけですよ。


スタブを作るのが面倒臭い!!


結構あるんですよ。
ちょっとの機能なのに一々スタブを作るなんて面倒でやってられない、とか。

だったら最初からDIコンテナなんて使うな、と言いたい所なのですが、現場の事情によることもありますので、
JMockitにて柔軟に対応していきましょう。

public class Sample01Test extends ContextManager{

 private Sample01 mockSample01;

 /** DIのモック  */
 @Mocked
 private SampleDao sampleDao;

 @Test
 public void testDoTest() {

                mockSample01 = getBean(Sample01.class)

  new NonStrictExpectations() {{
   Deencapsulation.setField(mockSample01, sampleDao);
        }};

        mockSample01.test();

 }

}

さて、JMockitは内部で「new」しているクラスも勝手にモック化する超強力ツールであることは第4回でご紹介しましたが、
DIコンテナの場合は「@Autowired」のアサーションで自動セットしており、内部でnewしているわけでは無い為か、普通にやってもモック化されません。

厄介ですね。

こういう場合は、荒技を使います。


  1. とりあえず普通にコンテナ経由でクラスを生成して、@Autowiredにより自動セットして貰う。
  2. 自動セットされた後でモックに上書き!!

privateフィールドを上書きするという超荒技です。


「Deencapsulation.setField(mockSample01, sampleDao);」というソースが、その上書きを行っている箇所です。


簡単に使えますね。
Javaクラス設計の根底をひっくり返す裏技ですが、そこはJMockitの黒魔術ということでご了承下さい。

終わりに

引き続き、現場からのフィードバック記事を連載します。

リクエストがあればこのような形で順次お答えしていきたいと思います。

2014年4月28日月曜日

【追加】最強モックツール JMockit その12 カバレッジオプション

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、私の現場ではこのブログを参考資料としてJUnit開発を進行中!!

そこで現場の皆様からリクエストが上がりましたので、ちょっと昔に巻き戻って情報を補強させて頂きます。

カバレッジオプション

JMockitにはカバレッジ出力機能が備わっている事は第2回でご紹介致しました。

JMockitのカバレッジ出力は簡単でして、jarファイルをインポートすれば勝手に出て来ます。
なので前回の記事では簡単に済ませてしまいましたが、ちょっとカスタマイズしたいという事情が出て来ましたので、オプションにてご説明しましょう。

カバレッジについて記述されている公式ページは以下になります。

機能のご紹介としては、概ね以下のような内容です。

coverage-output

カバレッジの出力モードのオプションです。
普通に実行するとHTMLファイルのカバレッジ結果が出力されますが、モードを設定することによって「シリアル」「マージ」といった機能が解禁されます。

「シリアル」「マージ」というのは、JUnitテストの分割で使用するパラメータです。

普通、JUnitのカバレッジというのは1回で全部出力しなければなりませんが、「シリアル」で実行するとcoverage.serという途中ファイルが出力されます。
これがシリアルファイルです。

複数に出力したシリアルファイルを合体させてHTML出力する機能を「マージ」と言います。

基本、JUnitは1クリックで全部出力するのが良いですが、「シングルトン」とか使ったプログラムですと、どうしても1回ではカバーしきれない場合があります。
そういう時に「シリアル」「マージ」のオプションを使うのが良いでしょう。

coverage-outputDir

カバレッジ出力先を指定するオプションです。
普通に実行すると実行パス直下に「coverage-report」というフォルダが出力されますが、違う場所に出力したい場合に使用します。

coverage-srcDirs

カバレッジ出力対象とすろソースフォルダのパスを指定するオプションです。
デフォルトでは「src」の配下を対象とするので、プロジェクト作成時に「srcフォルダ」「testフォルダ」の2つに分割するのが基本構成であるのは、第2回でご紹介したとおり。

しかし何らかの事情でsrcフォルダ配下にソース、という構成が取れない場合は、このオプションを使用して対処します。

coverage-classes

カバレッジ対象とするJavaファイルを正規表現で絞り込む機能です。
通常ですと全Javaファイルを走査してカバレッジを出力しますが、このオプションによって「特定パッケージだけ」等の対応が可能になります。

coverage-excludes

こちらはcoverage-classesの反対で、正規表現で特定Javaソースを除外する機能です。

coverage-metrics

カバレッジの網羅パターンを指定します。
デフォルトで実行すると「C0(行網羅)」でカバレッジが出力されますが、
オプションを指定することで「C1(分岐網羅)」「C2(条件網羅)」のモードに切り替えることが出来ます。

coverage-check

カバレッジ率をチェックするオプションです。

普通に実行すると単にカバレッジが出力されるだけですが、オプションを指定することにより「指定数値よりカバレッジが低い場合」に警告を出してくれるようになります。

オプション実行

如何でしたでしょう?
最強モックツールに相応しい、痒いところにまで手が届く充実したオプションシリーズですね。

しかし、基本的には「オプションを使わなくても良いようにプロジェクトを運用する」のが大事になるかと思います。
オプションを使用するということは、それだけ複雑になるということですから。。。

しかし、どうしてもオプションを指定して実行しなければならなくなることもありますので、Eclipseでオプション指定実行をする手順もこの記事にて掲載します。

実行の構成から指定する


EclipseからVM起動オプションを設定する場合、基本は以下のように実行の構成から指定することになるでしょう。


  • 右クリック⇒実行の構成⇒引数

ここから、「-Dcoverage-metrics=all」のように、頭に「-D」をつけた引数を入力すれば、それでオプション付きで実行したことになります。


しかしですね、これをやると「ファイル毎に起動構成を作らなければならない」という欠点があります。

JUnitテストなんて、色々なテストケースを何度も何度も実行するものですから、毎回毎回オプションを気にしながら実行するのは面倒極まりないと思います。

そこで、私としては「デフォルトVM引数」を指定するのをオススメします。

デフォルトVM引数指定

Eclipseには「いつも同じVM引数をつけて実行する」というデフォルト指定モードがあります。
VM引数指定が必要な実行があるなら、こちらにすることで手間を省くことをオススメしたいです。

  • ウィンドウ⇒設定⇒Java⇒インストール済みのJRE

VM引数はVM単位で付与しますので、実行時に使用するJavaVMを選び、編集をクリックして下さい。
すると、「デフォルトのVM引数」という項目があるので、ここに入れて実行すればOKです。



こうすることで、最初にデフォルト指定しておけば、いつも同じ設定で実行出来るようになりますので、テストコード開発も捗るでしょう。

終わりに

久しぶりのJUnit記事になりました。

現在、私は現場でこの記事をベースにJMockitを使ったユニットテスト開発を行っていますが、やっぱりJMockitは強力ですね。
出来ないことが無いというくらいに、何でもJMockitで貫き通せています。

次回も現場で出て来た要望への対応、「プライベート変数書き換え」をお送りします。

現場で使っているライブラリ「Spring」のDI機能をモック化する為の特殊対応です。

2014年4月8日火曜日

【GAE】スピンアップ問題4~Ajax作戦~

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、クラウド基盤「Google App Engine(以下、GAE)」の連載しています。

現在はGAE界最大の敵である「スピンアップ」のシリーズです。

本日はスピンアップ編の最終章、Ajax作戦についてです。

重いものは重い

さて、前回のおさらいからですが、GAEのスピンアップは重いものです。
限界までチューニングした所で数秒、現実的には7~8秒は必要とすると見込んで良いでしょう。

つまり、


  • 最初に表示した時、画面が10秒近くホワイトアウトしている。


こういう現象に陥ってしまうわけです。
今回はこの現象に対する対抗策に関する内容です。

我慢作戦

まあ、「スピンアップ」というのはインスタンスが起動する時だけ重いものですから、
最初の一回だけ我慢すれば、後は速いものですよ。

私は過去に負荷テストを行っていますが、
その時の実績によると、20~25人がF5アタックしているような状態でも1インスタンスで捌けてしまいます。

つまり、普通の人が普通にアクセスするペースだと、その10~20倍の250~500人程度は、スピンアップが最初の1回以外は発生しない見込みになります。

何が言いたいかと言いますと、

  • ホワイトアウトなんて滅多に起きないんだから我慢しろ。

というスタンスも十分アリってことですよ。

2回目にアクセスした時は速くなっているわけですから、ネット回線の一時的な不調と切り分け出来ないレベルだと思います。

しかし、我慢作戦では元も子もありませんから、真面目に技術的対処方法をご紹介します。
それは、Ajaxを使用することです。

Ajax作戦のカラクリ

GAEのスピンアップ問題から逃れることは出来ません。
そこでAjaxが活躍するのです。

  1. 先にHTMLファイルや画像などの静的コンテンツを表示する。
  2. 表示直後、OnLoadでAjax起動。くるくるダイアログを表示しつつ、非同期通信で動的コンテンツを取得。
  3. Ajaxにより動的コンテンツを表示。

この三段構えで表示することで、体感的に速く表示されるようになります。

1.静的コンテンツ表示

実はですね、GAEというのは、「Javaファイルが置かれているサーバ」と「HTMLや画像など静的ファイルが置かれているサーバ」は別々なのです。

そして、スピンアップが発生するのはJavaが置かれているサーバのみ。
HTML、CSS、JS、画像ファイルなど、静的ファイルの表示だけであればスピンアップは発生しないのです。

よって、①は常に超高速で表示されます。

これがAjax作戦の最大の秘訣です。

2.非同期通信

Ajaxの非同期通信によって、Javaにアクセスして内容を動的に表示します。
ここでスピンアップが起きるので、くるくるダイアログが10秒近く表示されていることになります。

つまり、トータルで待たされている時間は全く変わらないわけです。

ただ、①の時点で枠組み部分は表示済みになっており、処理中を意味する「くるくるダイアログ」も表示されていますので、
ホワイトアウト状態と比較すれば、待たされている人のストレスがは劇的に軽減されます。

3.描画処理

バックエンドの非同期通信でデータを取得しましたら、画面に出力します。


これにて画面が無事に表示出来ました。
これがGAE界で主流になっているAjax戦術の概要です。


Ajaxスキル必須


はい、ここでお分かりになられたかと思います。

Ajax作戦では、初期表示の際には静的ファイルであるHTMLを使用して表示します。
よって、jspは使用出来ません。
システム全体がAjaxに完全対応しなければならないのです。

上記には「我慢作戦という選択肢もある」と書きましたが、実際の所、GAE界ではこのAjax作戦が支配的なシェアを持っています。
GAE開発では、Ajax作戦が最適解であると言い切ってしまって良いでしょう。

つまり、開発者には「Javaスキル」と「Ajaxスキル」の両方が求められるわけです。

  • GAE開発は、JavaとAjaxの両方のスキルを持っていることが必須!!

プルダウンの中身を差し替えるだけみたいな粗末なAjaxではありません。
画面全体がAjax状態。
技術者はフルAjaxを実現するスキルを持っていなければなりません。


私はこの、「技術者の要求するスキルハードルの高さ」がGAE最大のネックだと思っています。
JavaとAjax、両方に精通している技術者というのは中々いません。


一般に、Javaというのは業務用アプリで多く使われる傾向にあります。
業務用アプリではAjaxは出番が少ないのです。

逆にAjaxが多く使用されるのはエンドユーザ向けシステム、例えばSNS辺りでしょうが、ああいうのはサイクルの速いスクリプト言語で書かれている事が常です。

つまり、GAE開発に必要とする「Java」と「Ajax」を両方揃えている技術者というのはレアケースということになります。


一応、「Android + HTML5のハイブリッドアプリ」の開発経験者なら期待出来る所ですが、それも技術者全体から見れば少数派でしょうし……。


「Java × Ajax」、これは意外にレアなのです。


終わりに

GAE最大の問題である「スピンアップ」について検証を重ねるうちに、
徐々にGAEの癖というものが分かってきましたね。

GAEは、普通のJavaWebシステム開発とは明らかに違う点が、確かにあります。

この為、GAE開発を行っている会社というのは、GAE専門特化型になっているケースが多いように見受けられますね。
スペシャリスト型でなければキツいのでしょう。

そんな、気軽に使うにはちょっとハードルの高いGAEですが、
次回はそれの有効活用法について検討したいと思います。

2014年3月31日月曜日

【GAE】スピンアップ問題3~ライブラリ厳選~

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、クラウド基盤「Google App Engine(以下、GAE)」の連載しています。

現在はGAE界最大の敵である「スピンアップ」のシリーズです。

ライブラリ厳選

さて、前回の振り返りになりますが、GAEのスピンアップが重い理由。それは「クラスロードが重い」からです。

つまり、高速スピンアップを行う為には、クラスロードを減らさなければならないわけですね。
そして、クラスロードを減らすために一番重要なことは、「クラスロードの少ないライブラリを選ぶ」ことです。

例えば、JavaのWebシステム開発で定番フレームワークで「Struts」というものがありますが、
あれはGAEを想定したライブラリではありませんので、クラスロードを減らすという発想がありません。

よって、クラスロードには非常に時間が掛かり、20秒くらい要するケースもあるとか。



20秒



画面をクリックしたら、まず20秒は真っ白にホワイトアウトしていて、それからメイン処理が走って、ようやく画面が表示されるという。。。

壊滅的遅さですね!!
これではせっかく訪れてくれた利用者も「サイトがダウンしているんだ」と思って帰ってしまうでしょう。

よって、GAE開発にはGAE専用にチューニングされたフレームワークを使う必要があります。

それが、私のオススメする「Slim3」です。

Slim3

Slim3とは、Seasarプロジェクトで有名なひがやすお氏が開発した、GAE専用フレームワークです。

と言っても、GAE専用特化フレームワークというのは、そんなに数はありません。
実質的に、GAE界はSlim3の独壇場になっていると言ってしまって良いと思います。

細かい話は横に置いて、とりあえずSlim3を選んでおけばハズレはありません。

では、さっそくSlim3をベースにスピンアップ時間を計っていきましょう。


計測

では、スピンアップ時間を計測していきます。

比較対象として、以下3種類を計測してみました。


  • ネイキッド:フレームワーク無し。GAE導入そのままの状態
  • Slim3:Slim3フレームワークのみを導入した状態
  • Slim3 + jsp:Slim3フレームワークでjsp出力する組み合わせ

その結果がこちらです。


種類ms
ネイキッド3826.2
Slim34658.6
Slim3 + jsp5587.6

ネイキッド:3826.2

「え~ッ!?」って感じですね。

ネイキッドとは、要するに何も無い空っぽの状態ということです。
GAE公式ライブラリをプロジェクトに入れて、真っ白な画面を表示するだけ。
これで3826.2msを要します。

空っぽでも4秒近くかかるわけです。
これ以上速くは絶対になりません。

これでもう、すでに一つの結論が出ましたね。



  • GAEのスピンアップはどうやっても遅いから、遅いことを前提でシステムを作れ。



これが一番重要です。
これを受け入れなければGAEは活用出来ません。

Slim3:4658.6


さて、我らの神ライブラリ「Slim3」ですが、「ネイキッド + 0.8秒」という数字になりました。
Slim3が使っている時間は0.8秒です。

Strutsの20秒とかと比較すると、如何に高速化されているかが分かるでしょう。

やっぱりSlim3は鉄板のライブラリです。(^_^)

Slim3 + jsp:5587.6


最後に「Slim3 + jsp」です。
急にjspを持ち出してしまいましたが、これも重用です。


  • jspのスピンアップは遅い。


jspは実行時にコンパイルを行っているわけですが、そのコンパイルエンジンである「jasper」の動きがGAEでは遅いと聞きました。
計測してみたところ、「slim3 + 0.9秒」です。

なるほど。確かにjspだけで1秒近く時間を食っています。
しかし、jspを使わなければこの時間は回避出来るわけですから、GAEではjspは使わないという判断が必要になりますね。


結論

さて、上記で結論が出ましたね。



  • どう頑張った所でスピンアップで4~6秒は待たされる。



これは痛い!!
普通に開発したら場合、「画面を開いたら4~6秒もホワイトアウトしている」という結果になるということですよ。


「ならダメじゃないか!!」と思われた方もいらっしゃるかもしれませんが、いえいえ、諦めるのはまだ速いです。


上記の遅さは「Javaのスピンアップ」の時間の話です。スピンアップさえ無ければ一瞬で結果が返ってきます。

遅いのは「Javaのスピンアップ」です。

つまり、



  • 最初にHTMLファイルだけパッと一瞬で表示して、ローディングのクルクル画像を表示しつつ、裏でAjaxでデータを取って表示する。



この裏技を使えば良いのです!!

実際には裏で4~6秒も待たされているわけですが、ホワイトアウトではなくローディング画像が出ているので、利用者のストレスはぐーんと低下します。

非常にセコい作戦ですが、これがGAE界の切り札、「Ajax戦術」なのです。


次回

次回は「Ajax戦術」についてご紹介致します。