+ All Categories
Home > Technology > Gaej For Beginners

Gaej For Beginners

Date post: 01-Nov-2014
Category:
Upload: shinichi-ogawa
View: 4,388 times
Download: 0 times
Share this document with a friend
Description:
1時間コースの入門編の資料です。
Popular Tags:
50
Google App Engine for Java 概要とか従来のJavaアプリケーション構築時との アーキテクチャの違い、注意点 shin1ogawa@株式会社トップゲート
Transcript
Page 1: Gaej For Beginners

Google App Engine for Java概要とか従来のJavaアプリケーション構築時との

アーキテクチャの違い、注意点

shin1ogawa@株式会社トップゲート

Page 2: Gaej For Beginners

アジェンダ• Platformの特徴• 特徴• 制限

•効率よく情報収集する為のパス•実装する時の環境• Datastoreサービス• 用語と特徴• 実装する時の注意点

• その他のサービス

Page 3: Gaej For Beginners

Platformの特徴

Page 4: Gaej For Beginners

Platformの特徴無料• 無料でサービスを開始できるので、「小さく始める」事が簡単にできる。• 無料の場合はDisk使用量や転送量に上限が存在する。

• 規模が大きくなってから課金を考えれば良い。• その時に、AppEngine上にデプロイしたアプリケーションを「スケール可能」な設計に変更する必要がない。

• Google Appsを使っている場合はAppsのドメインを適用する事ができる。

Page 5: Gaej For Beginners

Platformの特徴サーバの管理コストが不要• Googleの各種サービスを提供しているインフラの規模と質をそのまま利用できる。

• Googleが保守してくれている。• 物理的なインフラのコストはもちろん、ソフトウェアの設定・運用知識の習得・引き継ぎにかかるコストの削減ができる。

Page 6: Gaej For Beginners

Platformの特徴自動スケール• 負荷が高くなると自動的にスケールする。• どの程度の負荷でスケールが始まるか?の具体的な数値は不明。時々管理コンソールのログで、新たなJVMが起動されたなという事を知る事はできるが…。

• 必要な時だけPlatformの許容量が大きくなってくれる。• スモールスタート。

• 最初から規模が大きくなっても問題が出ないアプリケーションを作る事になる。• 例えAppEngineを使わなくなるとしても、今後の時代のためにもこのようなアーキテクチャを経験しておくのは良い事ではないか。

Page 7: Gaej For Beginners

Platformの特徴様々な機能がそれぞれ分散している• アプリケーションのメインとなるWebContainer• staticなリソース専用のFrontEnd• アプリケーションから使用するサービス• DatastoreService• MailService• URLFetchService• ImageService...

これらは全てそれぞれが別ノードに分散されていて・それぞれがスケールされる。

Page 8: Gaej For Beginners

Platformの特徴管理コンソール• アプリケーションが稼働した際の様々な状況等を確認する事ができる。

受信/転送データ量, CPU使用時間, DISKの使用量, APIの使用回数, アプリケーションのログ...等々。

Page 9: Gaej For Beginners

Platformの特徴“バージョン”の概念• 1アプリケーションに対して、複数のバージョンを存在させる事ができる。そのうち1つがデフォルトバージョン。• 例)リリースバージョン”release”をデフォルト、開発バージョンを”snapshot”にすると…• http://myappid.appspot.com/ ...最新バージョン• http://snapthot.latest.myappid.appspot.com/ ...開発バージョン

• バージョンごとに、別のアプリケーションとしてお互い影響を与えずにデプロイ、稼働できる。• datastoreは共有される• ひとつのアプリケーション内でpythonバージョンとjavaバージョンを混在させる事も可能。

Page 10: Gaej For Beginners

Platformの特徴Webコンテナ• JVMはJDK1.6ベースのHotspot Client VM• Jettyをベースとした実装だが、将来的に実装は入れ替えられる可能性があるらしい• Jettyは単なるJEEインターフェースとApp Engine間のアダプタ、としてしか機能していない

• JettyのComet機能も使えない(30秒制限もあるし、stream配信も不可)

• スケールする=リクエスト毎にJVMは違うノード上で動作する可能性がある、という点に注意が必要• 変更が前提のstatic変数は使ってはいけない

Page 11: Gaej For Beginners

Platformの制限制限の種類• 全体の量的な制限• Diskの使用量、ファイル数

• 期間単位の量的な制限• API単位の制限• Java的な制限

量的な制限は有料サービスにする事で上限を増やす事ができる(無くなる訳ではない)ようになるものがあるが、有料版でも変わらない制限も存在するので注意が必要。

詳細: http://code.google.com/intl/ja/appengine/docs/quotas.html

Page 12: Gaej For Beginners

Platformの制限Java的な制限• ファイルシステムへのアクセスができない。• Threadの生成ができない。• Socketの生成ができない。• JDKに含まれるclassの中で実装されていない物がある。• white-listとして実装されているclassが公開されている -> http://

code.google.com/intl/ja/appengine/docs/java/jrewhitelist.html

• 明示的にGCを実行する事が出来ない。• ...等々

Page 13: Gaej For Beginners

効率よく情報収集する為のパス

Page 15: Gaej For Beginners

効率よく情報収集する為のパスその2• Google App Engine Blog• AppEngine Cookbook• appengine java night (コミュニティ?)• ひがやすお氏のブログ

• スティルハウスの佐藤一憲氏によるtips集• ぶいてく社のブログ

• あおうさ氏のブログ• shin1ogawaのブログ

• 毎月第1,第3水曜日にIRC上で開催されるOffice Hour• irc://irc.freenode.net/#appengine• Google Calendar: [email protected]

Page 16: Gaej For Beginners

効率よく情報収集する為のパスその3

情報収集についての余談: AppEngineSDKのソースはまだ公開されていないので、逆コンパイルするしかない。が、利用規約で禁止されている。が、Hackathon参加時にGooglerに「逆コンパイルしないと発表できないんだが?」と質問してみたところ「調査に必要なら逆コンパイルするのは仕方無いんじゃね?でも改変した物を公開したりするのはダメだよ」という話でしたから、調査目的ならまぁ問題無いんでしょぅ。

今後、個人的なノウハウや、株式会社トップゲートの仕事で抽出したノウハウを整理・公開していく予定ですので期待して見守って頂けると嬉しいです。

• http://tech.topgate.co.jp/

Page 17: Gaej For Beginners

実装する時の環境

Page 18: Gaej For Beginners

実装するときの環境開発環境• Google Plugins for Eclipse(SDKを含む)• モジュールの依存、ローカル実行環境構成を含んだEclipseプロジェクト生成• AppEngineへのデプロイ!ほぼワンクリックで可能(お手軽)

• Ant(SDKに含まれている)• dev_appserverによるアプリケーションの起動• app_cfgを使ったAppEngineへのデプロイ

• Python版のSDK(JavaSDKはできない事ができたり)• indexのvacuum(IndexのErrorが発生したときとかに必要)• bulkloader(DatastoreのデータのExport/Import)

• NetBeansやIntelliJ IDEA用のpluginもある…らしい

Page 19: Gaej For Beginners

実装するときの環境テスト環境• 単体テストのためのスタブモジュールを使用する• SDKを起動して統合テストするのであれば、単体テスト環境と同じ方法で作成したlocal_db.bin(単なるバイナリファイル)をSDKから読み込む事ができる• スタブで作成したlocal_db.binにはAppIDが含まれているので、その辺りを少し細工してlocal_db.binを作成すればおk!

• Mavenでプロジェクトを構築する事も可能• mvnsearch というホストにappengineモジュールが配置されている。• shin1ogawaがMavenからSDKを起動するpluginも公開してます。

Page 20: Gaej For Beginners

実装するときの環境クラウド上• 今のところクラウド上での動作の調査等についてはロギングを使用して地味に調査するしか無いと思われる。

• ロギングには java.util.logging を使用する必要がある。• war/WEB-INF/appengine-web.xml で logging.properties のパスを指定する必要がある

• ローカル環境とデプロイ環境をダイレクトに直結するユーティリティもある ...shin1ogawaが公開しています

Page 21: Gaej For Beginners

実装するときの環境appengine専用の特殊なファイル• war/WEB-INF/• appengine-web.xml

...appengine用のアプリケーション定義ファイル

• datastore-indexes.xml ...手動で記述するindex定義• cron.xml ...cronジョブ定義• queue.xml ...taskqueueの定義• appegnine-generated/• datastore-indexes-auto.xml

...自動生成されるindex定義

• local_db.bin ...Datastoreがデータを保存するファイル

Page 22: Gaej For Beginners

実装するときの環境appengine-web.xml• セッション機能のon/offを設定する• https機能のon/offを設定する...1.2.6からデフォルトでon状態

• クライアントから直接アクセス可能なstaticファイルとして扱うリソースのパターンを設定する

• プログラムから読み込むファイルとして使用するリソースのパターンを設定する

• logging.propertiesのパスを設定する• 他は、通常のweb.xmlも使用する

Page 23: Gaej For Beginners

Datastoreサービス

Page 24: Gaej For Beginners

Datastoreサービス用語• Kind...RDBでいう「テーブル」• Entity...RDBでいう「レコード」• Property...RDBでいう「フィールド」• Index...RDBとほぼ同じ• Key...Entityの主キーの事• Filter...SQLでいうWhere句の条件指定みたいな事• equality filter...演算子で言う”=”でのFilter• inequality filter...”>”, “>=”, “<=”, “<“

• Entity Group...親子関係が構築されたEntityの固まり

Page 25: Gaej For Beginners

Datastoreサービス特徴• BigTableに保存される• ...がしかし、履歴機能は使えない

• RDBとの最大の違いは「スキーマレス」という事• 例(属性名=値で表現。keyという属性名が主キーって事で)• [0]id=1, name=”Hoge”• [1]id=2, height=170.5, weight=65.5• [2]id=3, tag=[“Apple”, “Java”]

• つまり、一行読み込み、読み込んだ中身を見て初めて構造が判明する。

Page 26: Gaej For Beginners

Datastoreサービス特徴• 書き込みが遅い• ちょっとしたEntityの保存でも80ms/1件かかったりする• RDBと同じくIndexのサイズの影響を受ける

• 読み込みの速度はEntity数に依存するのではなく、読み込む件数(検索結果件数)に依存する。

• クエリ?そんなものは無い。ファイルのスキャンができるだけ。• Queryというクラスが複数用意されているが、クエリという名前を持つだけの幻だと思っておこう(使うけどね)。

• インデックスを使ったファイルのスキャン、と理解する

Page 27: Gaej For Beginners

Datastoreサービス特徴• 書き込みが遅い• ちょっとしたEntityの保存でも80ms/1件かかったりする• RDBと同じくIndexのサイズの影響を受ける

• 読み込みの速度はEntity数に依存するのではなく、読み込む件数(検索結果件数)に依存する。

• クエリ?そんなものは無い。ファイルのスキャンができるだけ。• Queryというクラスが複数用意されているが、クエリという名前を持つだけの幻だと思っておこう(使うけどね)。

• インデックスを使ったファイルのスキャン、と理解する。

Page 28: Gaej For Beginners

DatastoreサービスEntity Group• 親子階層を構成したEntity同士の固まり。• RDBでいうRelationとは関係がない。• Java的な継承関係とも関係がない。• 例)

• 会社Kind:Entity:id=1• 組織Kind:Entity:id=2

• 個人Kind:Entity:id=3• 会社Kind:Entity:id=4

• 組織Kind:Entity:id=5

Page 29: Gaej For Beginners

DatastoreサービスKey• Entityを一意に識別するためのProperty• Long型(id)かString型(name)をKeyのIdとして使用できる。

• EntityGroupで親子関係を構成された場合は、親EntityのKeyを子Entityに引き継ぐ(含む)• 親Entity: Kind=Parent, id=1• 子Entity: Kind=Child, name=“[email protected]”• 上記の時の子EntityのKeyは以下のようになる。• Parent(1)/Child(“[email protected]”)

Page 30: Gaej For Beginners

DatastoreサービスProperty• Entityが保持する属性。• java.langパッケージに含まれるプリミティブな型• String型の最長は500文字まで

• Text型• 500文字を超えるString用

• Blob型• バイナリオブジェクト用

• List Property• コレクション型

Page 31: Gaej For Beginners

DatastoreサービスIndex• Propertyの並び順• Kind Index• KindをKeyでスキャンするためのIndex

• Single Property Index• ひとつのPropertyを対象にしたIndex

• Composite Index• 複数のPropertyを対象にしたIndex

• Text型、Blob型のIndexは定義できない

インデックス爆発(index explosion)と呼ばれる現象に注意!安易にComposite Propertyとかに複雑なIndexを適用すると危険。

Page 32: Gaej For Beginners

DatastoreサービスTransaction• 行単位の更新でACID特性を備えている• 基本的には後から更新した方が勝つ• JDOを使うと楽観的排他制御を行う事もできる• もちろんロジックで楽観的排他制御をしても良い

• ひとつのTransaction内ではひとつのEntityGroupに含まれるEntityの更新しかできない!• この点を考慮した設計が必要になる。

Page 33: Gaej For Beginners

DatastoreサービスQuery• 使用できる条件は ’<‘, ‘<=’, ‘==’, ‘>=’, ‘>’のみ• Likeは無い。前方一致は可能(startsWith())• 複数の条件はAnd接続のみ可能• Javaでは ‘IN’, ‘!=’ は使用できない• 複数のPropertyに対して条件を指定する事ができるが、

inequality filterが適用できるのはひとつのPropertyだけ• max()とかmin()とか、便利な集計関数は存在しない。• Joinも無い。

Page 34: Gaej For Beginners

DatastoreサービスQueryファイルスキャン• Indexで定義された特定のソート順で並んだ中から、特定の範囲のデータをブロックで転送する ! Indexを定義してソートされていなければ取得できない• Kind Index, Single Property Indexはローカル環境で実際にDatastoreへアクセスする事で定義が自動生成される。

• Composite Indexは datastore-indexes.xml に自分で定義してやる必要がある。

Page 35: Gaej For Beginners

DatastoreサービスNGワード• 連番• アプリケーションが実行されるノードが複数にまたがるため(?)、シーケンスな

ID等はサポートされていない。• 自動採番した番号は、1,2,3,1001,1002...のように採番される。

• 件数のカウント• 基本的には1000件以上を数える事が困難。

• JDOを使えば1000件以上でもカウントできるが、遅すぎて話にならない。

どちらも、スケールを前提にした場合に、実装するコスト・実行時のパフォーマンス、を考えてGoogleがあえて実装していないという事か。工夫のtipsもあるが「要求の実現にコストをかける”価値”がある機能なのか?」をよく考えた方が良い。

Page 36: Gaej For Beginners

Datastoreサービススキーマの設計について• 集計するような導出項目、カウンタは別途保持する• 更新時に計算結果や中間結果を計算しておく• クエリ結果のカウントは難しい

• ジョインが必要ならジョイン済みのKindを作成する• どの部分をEntityGroupとするか(ひとつのTransactionで処理したいか?)、が肝• 補償Transactionも考慮する

• インデックス爆発を起こさないように注意する

Page 37: Gaej For Beginners

Datastoreサービス実装• JDO, JPA, Low-level APIの3種類存在する• 個人的に、JPAはオススメできない• JDOが最もドキュメントが豊富で、既存のORMに慣れた人には良いかもしれない。• しかし、本来のDatastoreから離れている気もする

• 最もパフォーマンスを発揮するのは Low-level API• JDOも内部では Low-level API に接続している

• 最近はJDOを捨ててLow-level APIへ行く流れがある通常のJEEプラットフォーム上でも動作するように設計・実装すれば便利やん!...等と思っていた時期もありました。でもそれは設計をゆがめる事に繋がると思うんで、考えない方が良いかも。どーしても考えたいなら、実はLow-level APIが近道なのかも?

Page 38: Gaej For Beginners

DatastoreサービスJDO/Entityの定義@PersistenceCapable(identityType = IdentityType.APPLICATION)public class Entity { @Persistent @PrimaryKey(valueStrategy = IdGeneratorStrategy.IDENTITY) Key id;

@Persistent String value; @Persistent(defaultFetchGroup = “true”) Text text;

@Persistent(defaultFetchGroup = “true”) List<Child> children; @Persistent Key otherEntity;}

基本的な型以外は自動Fetchされない。自動Fetchしたいなら明示的に指定する。子EntityをListで保持するとOwnedな関係を構築できる。他のEntityの主キーを保持する事でUnownedな関係を構築できる。

Page 39: Gaej For Beginners

DatastoreサービスJDO/保存PersistentManager pm = ... // PersistentManagerをどこかから取得;Parent parent = new Parent();parent.set......List<Child> children = new ArrayList<Child>();children.add(child1);parent.setChildren(children);

try { Transaction transaction = pm.currentTransaction(); transaction.begin(); pm.makePersistent(parent); transaction.commit();} finally { if (transaction.isActive()) transaction.rollback();}

Page 40: Gaej For Beginners

DatastoreサービスJDO/クエリPersistentManager pm = ... // PersistentManagerをどこかから取得;Query query = pm.newQuery(Entity.class);query.setFilter(“name == param”);query.declareParameters(“java.lang.String param”);query.setOrdering(“name asc, key desc”);@SuppressWarnings("unchecked")List<Entity> list = (List<Entity>) query.execute(“hoge”);...

Key key = KeyFactory.createKey(“Entity”, “keyName”);Entity entity = pm.getObjectById(Entity.class, key);

Key key = entity.getOtherEntityKey();OtherEntity otherEntity = pm.getObjectById(OtherEntity.class, key);

Page 41: Gaej For Beginners

DatastoreサービスJDO/その他• オブジェクトの状態に注意する。• transient, hollow, persistent/detached• JDOHelper#getObjectState()で確認できる

• PersistenceManagerが永続化処理を行うタイミングを InstanceLifecycleListener でフックできる。

• PersistenceManager#execute()が返すListオブジェクトはserializeできないので注意

• PersistenceManager#retrieve[All]()で全てのPropertyを強制的にfetchできる。

Page 42: Gaej For Beginners

DatastoreサービスLow-level API/保存Entity entity = new Entity(“Kind”); // Low-level APIのEntityクラスentity.setProperty(“name”, “hoge”);entity.setProperty(“height”, 170.5);

DatastoreService service = DatastoreServiceFactory.getDatastoreService();service.put(entity);

Entity[] entities = ....service.put(entities);

属性と値のマップを格納するEntityオブジェクトを使用する。タイプセーフではなくなってしまうが、シンプルかつ速い。

Page 43: Gaej For Beginners

DatastoreサービスLow-level API/クエリQuery query = new Query(“Entity”);query .addFilter(“name”, FilterOperator.GREATER_THAN_OR_EQUAL, “hoge”) .addSort("name", SortDirection.ASCENDING) .addSort("__key__", SortDirection.ASCENDING);

DatastoreService service = DatastoreServiceFactory.getDatastoreService();List<Entity> entities = service.prepare(query).asList( FetchOptions.Builder.withOffset(0));

Key key = KeyFactory.createKey(“Entity”, “keyName”);Entity entity = service.get(key);

主キーに対応するproperty名は”__key__”と記述する。

Page 44: Gaej For Beginners

その他のサービス

Page 45: Gaej For Beginners

その他のサービスMemcacheService• よく使うデータ等をメモリ上にキャッシュしておく• 最大容量は不明(調査不足かも、ゴメンナサイ)• 有効期限を設定する事ができる

• cacheが存在する、という前提の設計をしてはいけない• 実装には JCache(javax.cache), Low-level APIの2種類が使用できるMemcacheService service = MemcacheServiceFactory.getMemcacheService();service.put("key", list);Object cachedObject = service.get("key");service.delete("key");

Page 46: Gaej For Beginners

その他のサービスcronサービス• 定期なタイミングに、特定の処理を起動する• 起動する処理はURLで指定する。つまりServletとして実装。

• 管理コンソールで確認が可能• デフォルトバージョンのみ有効

cron.xml<?xml version="1.0" encoding="UTF-8"?><cronentries> <cron> <url>/cron/fetch?name=jiemamy-sf</url> <description>fetch the jiemamy SF.jp timeline.</description> <schedule>every 1 minutes</schedule> </cron></cronentries>

Page 47: Gaej For Beginners

その他のサービスTaskQueueサービス• 処理(task)をキューに登録しておき、設定したキューの消化速度に応じて処理が起動される• 起動する処理はURLで指定する。つまりServletとして実装。

• 管理コンソールで確認が可能queue.xml<?xml version="1.0" encoding="UTF-8"?><queue-entries> <queue> <name>default</name> <rate>1/s</rate> <bucket-size>3</bucket-size> </queue></queue-entries>

Page 48: Gaej For Beginners

その他のサービス他にもありますが詳しい説明は省略• MailService• 送信、受信(ハンドラはServletとして実装)

• XMPPService• 送信、受信クライアント(ハンドラはServletとして実装)

• URLFetchService• ImageService• UserService• Google Accountの認証を行う事ができる。• 基本的にはGoogleが用意したページにリダイレクトするが、これを独自に実装したとか、そういった情報が最近 appengine-java MLに流れていた。

Page 49: Gaej For Beginners

まとめ

• 従来のUI設計、モデル設計の手法をうまく活用する…とか従来に近い方法で設計・実装できるように調整する…のではなく、素直に「手法が変わった」と受けとめる方が素直。

• スケールする(並列動作可能とか)アプリケーション、と考えるとどっちみちアプリケーションの設計手法は変わっていく。Google App Engineの”制限”はそのための良い”養成ギブス”、と前向きに捉えよう。

従来のUI設計、モデル設計、実装、の手法に引きずられてはいけない

Page 50: Gaej For Beginners

ご清聴ありがとうございました!

shin1ogawa@株式会社トップゲート


Recommended