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戦術」についてご紹介致します。




2014年2月24日月曜日

【GAE】スピンアップ問題2~クラスロード~

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

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

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

何で重いのか?

過去の記事でもご紹介した通り、スピンアップとはインスタンスの起動のことです。
これが非常に重い為に「インスタンスの度にユーザが待たされる」という状況が発生します。
これがスピンアップ問題です。

しかし、何でスピンアップがこんなに重いのでしょう?

これは、Javaプログラマーなら簡単に察しがつくと思います。

Javaって、起動が遅いですよね。

あれですよ、あれ。
C言語とかで作ったバッチだと一瞬でレスポンスが返って来るのに、Javaだと何秒も待ったりします。
でも、一回起動してしまえば、以後の処理は高速ですよね?

そう、Javaというのは、「初回起動時に重たい処理を全部済ませて、以降の処理は高速に行う」という思想の言語なのです。

つまり、インスタンスの起動処理であるスピンアップが重い原因は、Javaの初回起動の重さと直結したものなのです。

具体的には「クラスロードが重い」、これが原因です。

クラスロードとは


クラスロードとは、Javaのクラスを呼び出すこと。。。

まあ、当たり前ですが、普通の開発ではそんなこと気にしませんよ。
普通の開発では、その処理に必要なクラスを都度、普通に呼び出してコーディングすれば良いですからね。

その為、

「クラスロード? 意味は分かるけど、だから何だよ?」

みたいな感じに、イマイチ臨場感が無い単語だと思います。
しかし、スピンアップ問題を検討する上では、クラスロードの真の意味を理解して頂かなければなりません。

さっそく、クラスロード時間計測用のテストソースを作ってみました。
これで「クラスロード時間」とやらをチェックしてみましょう。

以下、ソース。

public class SpinUpTimeController extends AbstractWebapiController {

    /** logger */
    private Logger logger = Logger.getLogger(SpinUpTimeController.class.getName());

    /* (非 Javadoc)
     * @see jp.co.net.genesis.controller.webapi.AbstractWebapiController#doResponse()
     */
    @Override
    public String doResponse() throws Exception {
        
        logger.fine("1");

        //対象ユーザを取得
        UserService userService = UserServiceFactory.getUserService();
        userService.getCurrentUser();
        
        logger.fine("2");

        //DAOを生成
        UserSettingDao userSettingDao = new UserSettingDao();
        
        logger.fine("3");

        //キーを作成
        Key key = userSettingDao.createKey("endo@genesis-net.co.jp");
        
        logger.fine("4");

        //キーから既存レコードを取得する。
        UserSetting model = userSettingDao.getOrNull(key);
        if(model == null){
            model = new UserSetting();
            model.setKey(key);
        }
        
        logger.fine("5");
        
        //全角文字要素をURIエンコードする。
        model.encode();
        
        logger.fine("6");

        Map map = new HashMap();
        map.put("responseCode", ResponseCode.SUCCESS.responseCode);
        map.put("userSetting", model);
        
        logger.fine("7");

        Gson gson = new Gson();
        String result = gson.toJson(map);
        
        logger.fine("8");

        return result;

    }
    
    /* (非 Javadoc)
     * @see jp.co.net.genesis.controller.webapi.AbstractWebapiController#checkUser()
     */
    protected boolean checkUser() throws Exception{

        logger.fine("a1");
        
        if(!PermitAccount.isAfterAttestation()){
        }
        
        logger.fine("a2");

        if(!PermitAccount.isPermitted()){
        }
        
        logger.fine("a3");

        return true;

    };
}

ソースの意味としては、適当に作った処理の隙間隙間にログを出力して時間を表示するだけです。
「元々は何をする為のソースだったの?」とかは今回は関係お気になさらず。
時間だけがポイントです。

では、コイツに初回リクエストを投げて、スピンアップさせてみたいと思います。
結果はこちら。


合計で7862msも要してしまいました。
たった1リクエストに8秒。
これは遅い……。

でも、2回目のリクエストなら29msですので、約300倍の差が出ています。
如何にスピンアップが重いかがお分かりになるかと思います。

遅い原因は上のログを見れば分かります。
しかし、このブログの読者の方が上のログに目を凝らすのも面倒で読む気がしないかと思いますので、
私にてポイントをピックアップしてみました。

着目点は、こちら。


2→3の処理で0.2秒を要しています。

その0.2秒の処理で何をやっているかと言うと、こちら。

logger.fine("2");

//DAOを生成
UserSettingDao userSettingDao = new UserSettingDao();
        
logger.fine("3");


ただインスタンスをnewして作っているだけですね。
コンストラクタで何かやっているわけではありません。

ただ、純粋にnewしてインスタンスを作るだけで、0.2秒。

1クラスロードに0.2秒を要するのです!!

よって、どこぞよりダウンロードしてきたライブラリをインポートして、
そのクラスの機能を呼び出して、その中でクラスをロードして、とやっていけば、

0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2……。

スピンアップに7秒も8秒も使ってしまうのは、こういう理屈なのです。

終わりに


というわけで、スピンアップを高速化するには「クラスロードを減らす」、これがキーとなりそうですね。

次回は、具体的に「どうやってクラスロードを減らせば良いのか?」という点について検討してみたいと思います。

2014年2月18日火曜日

【GAE】スピンアップ問題1~始めに~

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

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

今回はGAE界の最大の敵、スピンアップについてご説明します。

スピンアップとは

まずは過去の記事で掲載したインスタンスの起動についておさらいしましょう。


GAEは、ユーザからのリクエストをトリガーにして「インスタンスの起動」という現象が発生します。

  • ゼロインスタンスの状態から、初めてアクセスが来た時
  • アクセス殺到により追加でインスタンスを立ち上げる時

このような時に発生する「インスタンスの起動処理」、これを『スピンアップ(spin up)』と言います。

激遅

さて、なぜこのスピンアップがGAE界最大の敵かと申しますと、それはもの凄く遅いからです。

開発していて、すぐに気がつきました。


  • 最初だけ妙に遅いな……。2回目からは速いんだけど……。


その時間、十秒近く。画面がホワイトアウトしているんですよ。

元からJavaのWebエンジニアである私には、心当たりがありました。
jspコンパイルです。

  • jspを使ったWebシステムで、デプロイの際にプリコンパイルを行っていないと、初回アクセス時にコンパイルが走って初回だけ重い。

これと酷似している現象であると直感的に察しがつきました。
しかし、調べてみたらスピンアップ問題はこれ以上に厄介な問題です。

jspコンパイル問題は「初回アクセスのみ」発生しますが、スピンアップは「インスタンス起動」の度に発生します。
つまり、何度でも発生するわけですよ。

ある程度のアクセス頻度で安定しているサービスならば、いつもインスタンスが立ち上がっているので問題にはなりませんが、「利用者の少ないサービス」「多過ぎるサービス」だとインスタンスの停止/起動を繰り返しますので、


「このサイト、最初はいつも遅いね」


という印象なサービスになってしまいます。

ここで、Webサイトの表示速度と人間の体感について、データを見てみましょう。

サイトを表示する際、どれくらい待たされると人はイライラし始めるのか、というアンケートがあります。

待ち時間比率
4秒超17%
3秒36%
2秒30%
1秒12%
1秒以下5%

何と、3秒待っただけで過半数の人は「遅い!!」と感じるようです。


  • 表示に3秒以上掛かるサイトは、閲覧される前にブラウザを閉じられる。


技術者にとっては厳しい世界ですね……。

ちなみに、上記3秒ルールは普通のサイトの話です。
アマゾンみたいな大規模通販サイトだと、0.1秒の違いでも如実に売上が変化するそうですよ。

システムの速度はスピードは売上に影響するんです!!

仕事しているとクソ遅いシステムの改修とかやらされてしまうことが多々ありますが、ああいうシステムは最低ですね。

真のWebエンジニアは、刹那の一瞬に命を賭けるオリンピック選手顔負けのスピード狂でなければならないのです。

終わりに

このように「スピンアップにより、最初の表示に10秒」とかでは話にならないわけです。
このスピンアップ時間を如何に短縮するか、これがGAEを活用する上での最初の大問題となるわけです。

今回の「スピンアップ問題シリーズ」では、このスピンアップについて調査し、掘り下げていこうと思います。
次回は、まずは現状調査。


  • なぜ、スピンアップはこんなに時間が掛かるのか。
  • 具体的に何秒くらい要しているのか。


この辺りから調査を始めていきます。

2014年2月4日火曜日

【GAE】インスタンス時間3

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

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

さて、前回の記事で、GAEの料金の中核は「フロントエンド・インスタンス時間」であることをご紹介しました。
Google本家の情報によると、一日200万プレビューくらいまでは無料で行けるそうです。

はてさて、本当にそんなレスポンスが出るのか、今回はちょっとDOSアタックを仕掛けて検証してみましょう。

負荷要件

さて、Googleの言う200万プレビューというのは、「効率が良いロジックである場合」の話です。
一回のリクエストが重ければ200万プレビュー無料は達成出来るわけ無いです。

この為、今回の負荷テストでは「標準的な効率的ロジック」に対し、多数のリクエストを投げる要件で検証してみようと思います。

具体的には、「テーブルの主キー検索1件」です。
一回のリクエストでテーブルの主キーを検索し、1レコードを取ってくるという処理は、
DBを使ったものの中でも最も標準的なものですからね。

この要件に対し、大量アクセスをかけて検証してみましょう。

1リクエスト速度

この負荷要件の場合、「1回のリクエストは何秒なのか?」が重用になります。

GAEだと、1リクエストの時間は標準でログに出力されておりまして、簡単に正確な時間が分かります。
それがこれ。



  • ms=26

何と、1リクエストで26msです!!

これは速いですね。
ちなみに、GAEの場合、どれだけデータ量が増えても主キー検索の速度は変わりませんので、安定してこの速度が出せます。

では、この要件で負荷テストを実施します。

アタックバッチ

DOSアタックには、私が自前で作ったマルチスレッドのリクエスト送信バッチを使います。
指定本数だけリクエスト送信スレッドを立てて、無限ループでリクエストを送りまくるという単純なバッチです。

1スレッドの時は、同時に1ユーザがアクセス中。
10スレッドの時は、同時に10ユーザがアクセス中。

と見なすわけです。

お見せする程の価値のあるソースではありませんが、一応以下に張っておきます。

public class DosAtackBatch extends AbstractBatch {

   /** logger */
   private Logger logger = Logger.getLogger(DosAtackBatch.class);

   /** 送信先URL */
   private String URL = "http://genesis-gae-service.appspot.com/webapi/develop/SpinUpTime";

   private int threadCount = 1;

   private int attackTime = 30000;

   private static AtomicInteger count = new AtomicInteger();

   /**
    * @param args
    * @throws Exception
    */
   public static void main(String[] args) throws Exception {

      DosAtackBatch batch = new DosAtackBatch();
      batch.excute(args);

   }

   /*
    * (非 Javadoc)
    *
    * @see jp.co.tacy.batch.AbstractBatch#doExcute(java.lang.String[])
    */
   @Override
   public void doExcute(String[] args) throws Exception {

      final Date dateBefore = new Date();

      for(int i=0;i<threadCount;i++){

      Thread thread = new Thread() {
            /*
             * (非 Javadoc)
             *
             * @see java.lang.Thread#run()
             */
            @Override
            public void run() {

               while (true) {
                  RequestSender sender = new RequestSender();
                  try {
                     sender.sendPost(URL, null);
                     Date dateAfter = new Date();

                     long responseTime = dateAfter.getTime() - dateBefore.getTime();

                     logger.info("アタック回数:" + DosAtackBatch.count.incrementAndGet());
                     logger.info("レスポンス時間:" + responseTime);

                     if(responseTime > attackTime){
                        break;
                     }

                  } catch (IOException e) {
                     logger.fatal("エラーが発生しました。", e);
                  }
               }
            }
         };

         thread.start();
      }

   }

}

計測:1スレッド

では、このバッチを使ってアタックをかけてみます。

  • スレッド数:1
  • 実行時間:30秒
  • リクエスト総数:108回

結果はこの通り。



1インスタンスしか上がっていません。
まあ、1スレッド=1ユーザという想定ですから、1ユーザしかアクセスしていないのであれば、1インスタンスで足りるのは当然ですね。

計測:10スレッド

次は、一気に10ユーザ行ってみましょう。

  • スレッド数:10
  • 実行時間:30秒
  • リクエスト総数:1024回

どうやら私の作ったバッチは10スレッドを立ち上げても、1スレッド辺りのパワーは落ちないようです。
それなりに良いPCを使わせて頂いております。(^_^)

さて、肝心の結果ですが、何とこれでもインスタンス数は1コでした。
  • 10ユーザがF5アタックしているような状態でもビクともしない!!

計測:20~25スレッド


「性能が凄いのは分かったから、いつになったら変化するんだよ?」

という感じでしょうから結論を述べますと、25スレッドで変化が出ました。

  • スレッド数:25
  • 実行時間:30秒
  • リクエスト総数:2104回

25スレッドで実行すると、インスタンスが2つ立ち上がります。



流石に25スレッドともなるとPCの性能が追いつかなくなってきて、1スレッド辺りのパワーが下がってきているようです。

なので、多少数値が曖昧ですが、概ねの性能が出て来ました。

  • GAEは、20~25人のユーザがF5アタックしているような状態まで、1インスタンスで対処することが出来る。

キリの良い20スレッドで再計測しましたら、以下の実績で1インスタンスでした。

  • スレッド数:20
  • 実行時間:30秒
  • リクエスト総数:1564回

というわけで、この20スレッドの数字が1インスタンスの上限値と位置づけて、算出してみようと思います。


  • 30秒で1564リクエスト
  • 1分なら1564×2=3128リクエスト
  • 1時間なら3128×60=187680リクエスト
  • 1日なら187680×24=4504320リクエスト
  • 1ヶ月なら4504320×30=135129600リクエスト

1日で450万、1ヶ月で1億3000万リクエストまで無料!!

という概算見積もりとなりました。


まとめ

さて、私の計測結果だと、Googleの主張する1日200万プレビューの2倍以上の実績を叩き出してしまいました。

まあ、私がテスト用に作った機能は超軽量ですから、現実的にはもうちょっと重くなるのが普通でしょう。
となると、1日200万プレビュー無料という謳い文句は、妥当な数字であると言うことが出来るかと思います。

結論としましては、

  • GAEは1日200万プレビューまで無料まで実現可能

をキャッチフレーズにしても問題は無いのではないかと。

ただし、この数字はザクッとした見積もりですので、現実にプロジェクトとして導入するなら、もうちょっと細かく計算したいです。

私の計測の場合、1リクエストの処理時間が26msで1日450万という結果でした。ここから比例計算で、

  • 1リクエストの平均が26msの場合:450万
  • 1リクエストの平均が58msの場合:200万
  • 1リクエストの平均が100msの場合:116万
  • 1リクエストの平均が1000msの場合:12万

みたいな感じに算出することが出来ます。
各自、自分の作ったアプリの1リクエストの時間を見て、そこから上の比例計算で割り出してみると良いかと思います。

終わりに

今回の解析で、GAEのコスト効率が概ね見えてきましたね。

ちなみに現在、私はこのブログと平行して社内用を想定したアプリを作っているのですが、一番重い処理でも1リクエストの時間は200msです。

ここから考えて、

  • GAEに最適化した要件であれば、1日200万アクセスまで無料
  • 多少のカスタマイズを入れた業務用アプリなら、平均して1日50万アクセスくらいまで無料

これくらいが現実的な見積もりなんじゃないかと思っています。

それ以上になると有料になりますが、その場合は1時間当たり0.08ドルですから、
業務用としてWebサイトを運用する場合、

  • 1日50万アクセスくらいまで無料。
  • 1日100万アクセスなら毎月5000~6000円くらい。
  • 1万円出してくれれば1日120~130万アクセスくらい行けそう。

という予算感覚です。

こりゃ安いですね。
ビジネスモデルとしての展望が見えてきそうな感じです。

この先もまだまだ気合い入れて勉強していきたいと思います。

2014年1月29日水曜日

【GAE】インスタンス時間2

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

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

今回も前回に続いて、「インスタンス時間」について理解を含めていきたいと思います。

おさらい

まずは今までのおさらいから。

GAEは「従量課金制」ですので、消費するほどにお金が多く発生します。

①アクセスが増える。

②インスタンスが立ち上がる。

③インスタンス時間を消費する。

この3段ロジックです。

例えば、アクセス者が1人だった場合、インスタンスは1コしか立ち上がりません。

最初にリクエストが来て、この時にインスタンスが立ち上がると当時に、一定時間かけて処理を行います。
処理が終了した後、その瞬間にインスタンスが即死するわけではなくて、しばらくは「アイドル状態」で待機します。
以下みたいな状態のイメージです。
赤色の部分が処理中、白色の部分がアイドル中のイメージです。


アクセス数が少ないと「インスタンスはアイドル状態で立ち上がっている」という状況になりますので、アイドル時間中はインスタンスを無駄遣いしていることになりますね。
出来ればこの無駄は省きたい所ですが「アイドル状態で待ち構えているから、リクエストが来た時に高速に反応できる」という側面もあるのです。多少の無駄遣いは仕方がありません。
インスタンスが死んでいる時にアクセスが来ると、「スピンアップ」という重いインスタンス起動処理が走ります。アイドル状態からの起動と、死んでいる状態からの起動ではスピードが全然違います。
(「スピンアップ問題」はGAEの数ある制限の中でも頭を悩ませる超重要問題なのですが、それについては今後に記事にしたいと思います。)

とにかく、アクセスが同時に1人だった場合は、こんなイメージです。

次に、これが2人になると、以下のようになります。


ご覧のように、少し待って「1インスタンスで2人捌く」という結果になります。

「1人なら1インスタンス。2人なら2インスタンス。100人なら100インスタンスだ!!」

なんて効率の悪いことにはなりません。
少ない人数でしたら1インスタンスで捌いてくれます。

ただし、待ちが発生しますけどね。

同時にアクセスが来たら、片方にはもう一方の処理が終わるまで待って貰います。
その待ち時間を「Pending Latency」と言います。

  • 待ち時間が短いうちは1インスタンスで捌く。
  • 「待ち時間長過ぎ!! 遅えぇぇぇぇぇッ!!」となったら新規インスタンス起動

こういう関係なのですよ。
スーパーのレジに人が溜まってきたら、新しい人が別のレジに入って捌くのと似たようなものです。

アクセス殺到時のイメージが以下の図です。


最初のうちは1インスタンスで頑張ってくれていますが、アクセスが殺到したら、アクセス者を待たせないようにもう1コインスタンスを起動して、そっちにもアクセスを振り分けるという意味です。

まとめますと、

  • 1リクエストの処理速度
  • リクエスト数

GAEのインスタンス時間は、この2つの要素の直撃を受けます。
リクエスト数を減らすことは出来ませんので、「1リクエストの処理速度」を出来る限り高速化すること。
この辺りがGAEのコツです。

「動けば良い」などというゴミロジックは、GAEでは厳禁!!
インスタンス消費時間という形で、明白にお金という形で報復を受けます。
高速ロジックこそがGAEの正義です。

ロジックを組むプログラマーの腕前が会社の経営を左右するかもしれないのです。

塩梅をつける

と言っても、実は「設定」でこのインスタンス消費時間を下げることも可能なのです。

上記に「アイドル状態」「待ち時間」という二つのキーワードが登場していますが、これらを手動で塩梅をつけることでインスタンス消費時間を下げることが出来ます。

アイドルインスタンス調整

アイドルインスタンスとは「次のアクセスに備えて待機しているインスタンス」のことです。アイドル状態で多数のインスタンスを控えさせておけば、急にアクセスが来た時でもすぐに対処出来ます。
と言っても、アクセスが来ていない場合は単なる穀潰しです。
「多少時間がかかっても、アクセスが来てから新規インスタンスを立ち上げれば十分」というの論理がOKであれば、「最大アイドルインスタンス数を下げる」という設定を行うことで、穀潰しを抹殺することが出来ます。
但し、その分だけ「スピンアップ」というインスタンス起動処理の発生頻度が多くなりますのでご注意を。

待ち時間調整

上記には「アクセス者の待ち時間が長くなったらインスタンス起動」とありますが、「それって具体的にどれくらい待ち時間が長引いたらインスタンスが起動するの?」という話です。
0.1秒待っただけで新規インスタンスを起動するようでは、そりゃインスタンスは些細なことでポンポン立ち上がってコストを大量消費してしまいます。
「5秒や10秒くらい大人しく待ってろ」というの論理がOKであれば、「許容待ち時間を長くする」という設定を行うことで、過剰なインスタンス起動を回避することが出来ます。



これらの設定を行う個所が、アドミンコンソールの「Application Settings」です。



ここに「Max Idle Instances(最大アイドルインスタンス数)」「Min Pending Latency(最少待ち時間)」がありますね。
デフォルトではこれは「Auto」になっていますが、「Auto」というのは「最高パフォーマンス設定」のことで、この状態が一番速い代わりに高価です。Autoは本当に高速で、0.1秒待っただけでも次が上がってしまうような超高速モードらしいです。

これを最低設定の「Max Idle Instances:1」「Min Pending Latency:15s」まで手動で下げてしまえば、パフォーマンスが低下する代わりにインスタンスの起動が抑制され、結果コストが安く出来るのです。

「待ち時間15秒とか遅過ぎだろ!! でも3秒くらいなら許せるかな」みたいに、そのサービスの保証水準を鑑みて、各自調整するのが良いでしょう。

終わりに

以上が、インスタンス時間に関連する基礎知識でした。

しかし、これはあくまで「理論上の話」です。
次回は、いっちょ、自前でDoS攻撃を仕掛けて負荷テストを決行してみたいと思います。

一体、どれくらいのアクセスを捌くことが出来るのでしょうね。。。

2014年1月24日金曜日

【GAE】インスタンス時間1

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

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

今回はGAEのお金関連の話でも中核中の中核、「インスタンス時間」についてご紹介しましょう。

基本

まずは以下URLがGAEの料金について説明したページです。


GAEの課金制度は、基本的には「従量課金制」です。
電気代や水道代と同じく、多く使った人程にお金を多く払うという公正公平、かつシビアな制度でございます。
この為、安い値段、突き詰めれば無料でGAEを使う為には、GAEを節約しながら使っていく必要があるのです。
この辺りも電気、ガス、水道と同じですね。

とは言っても、

・水道の消費量。⇒流した水の量で決まる。⇒水を無駄に流さない。
・電気の消費量。⇒消費した電力量で決まる。⇒使っていない電気を消す。

みたいに、電気や水道ならば節約する為に何をすれば良いのか分かり易いのに対して、
GAEの節約とは何をすればいいのかピンと来ないですよね。

今回はそのお話です。

クォータ(Quota)

さて、GAEというのは、インターネット上に接続されている複数のマシンをみんなで使い回すことによって資源を有効活用するシステムです。
この為、


  • 「一人で勝手に無駄遣いするなよ」
  • 「あんまり使う奴は金払って貰うからな」


という運用になるのは至極当然のこと。
ジャイアニズムという声もありますが、我々に文句は言えません。
この場合の「各サービス毎への割り当てリソース」のことを「クォータ(Quota)」と呼びます。

このサービス毎のクォータ量を超えてしまうと「課金発生」「システムダウン」「機能低下」など様々なデメリットを被ってしまうことになります。
(容赦無し)

そのクォータにつきましても色々な種類がありまして、

  • フロントエンドインスタンス時間
  • バックエンドインスタンス時間
  • データストア書き込み回数
  • メール送信数

書き切れないくらい沢山あります。
それらどのクォータがオーバーしてしまってもNG。シビアな世界です。

自分のサービスがどれくらいクォータを消費しているかについては、自分のダッシュボードのメニューに出ている「Quota Details」からご確認ください。以下のような画面です。


実に多岐に渡る項目があるのが良く分かるかと思います。
全部理解しろと言われても、これは苦しい……。
とは言え、実質的にこれら全項目をパンパンまで使うなんてシチュエーションは考えられず、
大抵はこの中の項目のうち、「良く消費する一部項目だけがネックになる」という運用になるでしょう。

要するに「自分が使う部分だけ分かっていればOK」なのです。


以上でお分かり頂けたかと思いますが、GAEの従量課金制は厳密にはこのクォータ消費量を理解して算出しなければなりません。

しかし、最初からそんな細かい数字に目くじらを立てても効率が悪いです。
最初は重要部分から押さえることをお勧めします。

実は、GAEの多岐に渡るクォータのうち、「中核」と言って良い代表的なクォータが存在します。
GAE初心者は最初にまずこのクォータ「フロントエンド・インスタンス時間」を押さえると良いでしょう。

フロントエンド・インスタンス時間(Frontend Instance Hours)

「フロントエンド・インスタンス時間(Frontend Instance Hours)」とは、簡単に言ってしまえば「サービスが動いている時間」です。

GAEというのは、「アクセスが来るとインスタンスが起動し、アクセスが来ないとインスタンスが寝る」という動きをします。
この「インスタンスが起動している時間」のことを「フロントエンド・インスタンス時間」と言います。
(以下、「インスタンス時間」と略称でご説明します。)

一応注意ですが、「インスタンスが起動している時間ということは、1日最大で24時間だな」という計算ではありませんのでお間違え無く。
我々SE界には「人日 or 人月」という単位で予算を組みますが、それと全く同じ。

「インスタンス時間=インスタンス起動時間×インスタンス数」

となります。
よって、2インスタンスが24時間フル稼働していたら48インスタンス時間消費というわけですね。

そして、GAEの無料枠は28インスタンス時間ですから、これではオーバーしてしまいます。

無料枠は28インスタンス時間。つまりこのままでは、

「無料ユーザーは1インスタンスしか使えないってことじゃないか!!」

という意味になってしまいますね。
しかし、そこはGAEの「自動スケールアップ機能」が活躍してくれます。

  • アクセス数の多い時間帯は多数インスタンスを起動する。
  • アクセス数の少ない時間帯は少数インスタンスで済ませる。
  • アクセスの無い時間帯はゼロインスタンス。

こういう機能がありますので、「全然使っていないのにインスタンス時間だけ垂れ流し」という事態は避けられます。
利用者のいない時間帯はインスタンス時間を消費しません。
代わりに、利用者が多い場合は、いつもより多めにインスタンス時間を消費します。
28インスタンス時間という限りある資源はちゃんと有効活用出来るのです。

終わりに

となってくると次に疑問に思うのは、

「そのアクセス数の多い少ないってどうやって判断してるの?」
「インスタンスの同時起動数はどうやって決定されているの?」

この辺でしょう。

その辺りにつきまして、もう少し掘り下げていこうと思います。

2014年1月20日月曜日

【GAE】スタート編4 Slim3導入

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

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

今回はMVCフレームワーク「slim3」を導入します。

slim3とは

slim3はseasarの開発者であるひがやすを氏が開発したMVCフレームワークです。

公式サイトにも紹介されているGAE3大フレームワークの一つです。


元からseasar支持者である私は迷わずこれを選択。

日本語の非公式サイトもあって非常に助かります。
https://sites.google.com/site/slim3documentja/

Google検索でも結構情報が出て来ますし、今の所はこのslim3を導入するのが最適だと思います。

ただし、非公式サイトも更新が滞っているのかリンク切れしている箇所が複数アリ。
内容もザッとした触り程度で詳しい所までは載っていないので、このブログなどでフォローしていきたい所です。

slim3導入

slim3はEclipseプラグインをインストールするだけで導入可能です。

インストール用のロケーションはこちら。
http://slim3.googlecode.com/svn/updates/

以下の手順でEclipseにインストールして下さい。

  1. Eclipseを起動する。
  2. メニュー→ヘルプ→新規ソフトウェアのインストール
  3. [追加]からロケーションを追加

インストールしたらEclipseを再起動して下さい。

長きに渡る環境構築関連の記事でしたが、これでようやくプロジェクト作成に入れます。

プロジェクト作成

では、いよいよプロジェクト作成に入ります。

Eclipseのプロジェクト新規作成により、Slim3プロジェクトを選択して作成して下さい。


途中にGWTという文字が眼に付きますが、こちらはGoogle Web Toolkitというフレームワークと併用する為のモードです。
今回はGoogle Web Toolkitは使いませんので、普通のslim3を選択します。


これでプロジェクト作成完了です。

ルートページ作成

細かいことはさておき、とにかくまずはインターネット上へ展開してみましょう。
今回はルートページ「index.html」を作成します。

war直下に適当にファイルを作って置いて下さい。


デプロイURLセット

次に、デプロイ先のURLをセットします。
遡ること第2回で作成したプロジェクト名を指定して下さい。

  1. プロジェクトを右クリック→プロパティ→Google→Appエンジン


デプロイ実行


いよいよデプロイします。

  1. プロジェクトを右クリック→Google→Appエンジンへデプロイ

GAEにログインしていない場合はログイン画面が出て来ますので、ログインして下さい。

これでデプロイが始まります。
これでようやくアプリを世界に向けて公開……出来ないんですよね~。(泣)

いくつかエラーハンドリングしてみましょう。

エラーハンドリング

Javaのバージョンが違う

java.lang.UnsupportedClassVersionError: com/google/appengine/tools/development/LocalJspC : Unsupported major.minor version 51.0

これが出て来たら、それはJavaのバージョンが違うんですよ。
自分のプロジェクトをよく見て下さい。VMにjava6が指定されていませんか?
GAE SDK1.8.8はjava7、それもJDK7でしか動きませんので、お間違え無く。

JREを使っている

java.lang.RuntimeException: Cannot get the System Java Compiler. Please use a JDK, not a JRE.

これはJDKではなく、JREを使っているという意味です。
GAEはJDKでなければ動きません。

遡ること第1回でダウンロードしたJDKを使います。
ポイントは「Eclipseプラグインが呼び出すJavaをJDKにする」ということです。
つまり、Eclipseの起動に使用するJavaの変更です。
プロジェクトが読み込んでいるJavaのことではありません。
プロジェクトを動かすJavaではなくて、デプロイコマンドをキックするJavaをJDKにしろ、という話ですので、お間違え無く。

インストールしたEclipse内にあるeclipse.iniを開きまして、一番最初に以下を追加します。

-vm Eclipse起動用Javaのパス


パスを指定したらEclipseを再起動します。

成功!!

これでもう一回デプロイを試みれば、そろそろ成功してくれたのではないでしょうか?
私の環境では表示出来ました。


自動的にブラウザが立ち上がるはずですが、立ち上がらなかった場合は以下コンソールのステータスが「Running」になっていますので、それをクリックして下さい。

https://appengine.google.com/

ようやくデプロイまで漕ぎ着けました。
ここまで来るのは大変でしたが、一回出来てしまえば、後はクリック数回でいつでも簡単にデプロイ出来るようになります。

一応、これでデプロイ成功なはずですが、この作業の過程で偶々出たエラーメッセージもついでに以下に記載しておきます。
このブログの手順通りにやれば出ませんが、一応掲載です。

EclipseのバージョンとSDKのバージョンが合っていない

java.io.IOException: Jar C:\Users\User\AppData\Local\Temp\appcfg7254171904073358945.tmp\WEB-INF\classes\appengine-api-1.0-sdk-1.8.0.jar is too large. Consider using --enable_jar_splitting.

これは文言からすると、デプロイオプション「--enable_jar_splitting」を指定しろという意味に受け取れます。
しかし、「そのオプションを指定すれば解決するはずだ」と考えるのは大間違い。
これが出ると言うことは、EclipseのバージョンとSDKのバージョンが合っていないのです。
バージョンが合っていないからオプションが効いていないというだけの話なのです。

Eclipseを最新にするか、SDKを切り戻すか、何らかの手段でバージョンを合わせて下さい。
以前の記事にも書きましたが、GAEは「Eclipseのバージョンに敏感な影響を受ける」という特徴があります。
「Eclipseなんて4.2でも4.3でも大して変わらんだろ」という考えは、普通の開発ならそうですけど、GAEではNG。
動きません。
キチッと合わせてください。

終わりに

ようやくデプロイまで到達しましたね。お疲れ様でした。。。

環境セットアップ周りはGAEに限らずシステム開発では詰まりやすい所でして、私もここまで漕ぎ着けるのに大変苦労しました。
しかし、一度手順を確立してしまいさえすれば、案外何てことは無いものだと分かるかと思います。

これでスタート編は終了です。

次回からは実装関連の検証を進めてみます。