2010年3月6日土曜日

#appengine javaでcapability serviceを利用する

capabilityサービスを使用すること自体は、今回作ったモジュール(ProtocolBufferの定義それを使用するサンプル)をそのままコピーするなどして使えば簡単にできます。このエントリは、普段JavaでAppEngineを使う人も、Pythonコードを読むと色々役に立つよ、というような内容になってますので、そのあたりに興味が無い人は読んでもあんまり面白くないと思います。Java, Pythonの関係だとその逆も言えるんですけどね。AppEngineは今のところJavaとPythonがありますが、どちらかだけじゃなくて両方触っておくのが一番です。AppEngineJavaを深く触っている人にはSDKのpythonコードもよく参照している人が多いです。

本題

App Engineのサービスがメンテナンスモードの時に、例えばデータストアが読み込み専用になったりします。この時、データストアに対して書き込み操作を行うとCapabilityDisabledExceptionという例外が発生します。これについて、python版ではCapabilitySet(サービス名).is_enabled()のような方法で特定のサービスの稼働状況を調査することができるという事がNick Johnson さんのブログでHandling downtime: The capabilities API and testingというタイトルで紹介されています。失敗させて判断するよりはこういった方法で確認できた方がキレイですね。しかし、JavaにはこのAPIが存在しません。これは残念。しかしPython版のSDKのソースを見ると、capability_serviceというリモートサービスのIsEnabledという機能を呼び出しているだけだという事がわかりますので、これであればJavaであれPythonであれ、言語に関係なくこの機能を使用できるという事がわかります。必要な事はリクエストするバイト配列とレスポンスを受けるバイト配列それぞれについて、どのようにオブジェクトを組み立てれば良いのか?という事だけです。これさえできればJavaからも使用出来そうです。

AppEngineではリモートサービスとのデータのやり取りは全てProtocolBufferでシリアライズされたバイナリを使って行われるので、今回の「capabilityサービスをJavaからも使う」という目的に必要な作業は以下のようになります。ProtocolBufferについては、公式ドキュメントが一番参考になります。

  1. python版のsdkのソースコードから、リクエスト・レスポンスそれぞれのデータ構造を推測する
  2. リクエスト・レスポンスそれぞれのProtocolBuffer定義ファイルを作成する
  3. protocで定義ファイルをコンパイルし、Javaで使用できるクラスを生成する
  4. capabilityサービスを使ってみる!

データ構造を推測する

pythonコードを参考にすると、IsEnabledRequest, IsEnabledResponseというオブジェクトを使う事がわかり、IsEnabledResponseで使用されているCapabilityConfigというオブジェクトも必要そうです。

protocolbufferの定義ファイルを作成する

上記のpythonコードから推測した定義は以下のようなカンジです。完全に正しいかどうかはわかりませんので、もっと厳密に作りたい人はもっと正確な定義を書いてしまえばイイと思います。

message CapabilityConfig {
  enum Status{
    ENABLED = 1;
    SCHEDULED = 2;
    DISABLED = 3;
    UNKNOWN = 4;
  }
  optional string package = 1;
  optional string capability = 2;
  optional Status status = 3;
  optional string internal_message = 4;
  optional string admin_message = 5;
  optional string error_message = 6;
  optional string scheduled_time = 7;
}

message IsEnabledRequest {
  required string pacakge = 1;
  repeated string capability = 2;
  repeated string call = 3;
}

message IsEnabledResponse {
  enum SummaryStatus {
    ENABLED = 1;
    SCHEDULED_FUTURE = 2;
    SCHEDULED_NOW = 3;
    DISABLED = 4;
    UNKNOWN = 5;
  }
  required SummaryStatus summary_status = 1;
  optional int64 time_until_scheduled = 2;
  repeated CapabilityConfig config = 3;
}

Javaのクラスを生成し、使ってみる

protocコマンドでJavaソースを生成して取り込んだ後、私の場合はローカル環境から直接Production環境に接続する方法で試してみました。ソースの該当箇所へのリンクをふたつほど置いておきます。

実行結果は標準出力に出力しましたが、以下のようになりました。

datastore_v3
--start dump of protolbuffer--
summary_status: ENABLED
config {
  package: "datastore_v3"
  capability: "*"
  status: ENABLED
}
config {
  package: "datastore_v3"
  capability: "write"
  status: ENABLED
}
--end dump of protolbuffer--
memcache
--start dump of protolbuffer--
summary_status: ENABLED
config {
  package: "memcache"
  capability: "*"
  status: ENABLED
}
--end dump of protolbuffer--
taskqueue
--start dump of protolbuffer--
summary_status: ENABLED
config {
  package: "taskqueue"
  capability: "*"
  status: ENABLED
}
--end dump of protolbuffer--
user
--start dump of protolbuffer--
summary_status: ENABLED
config {
  package: "user"
  capability: "*"
  status: ENABLED
}
--end dump of protolbuffer--
blobstore
--start dump of protolbuffer--
summary_status: ENABLED
config {
  package: "blobstore"
  capability: "*"
  status: ENABLED
}
--end dump of protolbuffer--
mail
--start dump of protolbuffer--
summary_status: ENABLED
config {
  package: "mail"
  capability: "*"
  status: ENABLED
}
--end dump of protolbuffer--
urlfetch
--start dump of protolbuffer--
summary_status: ENABLED
config {
  package: "urlfetch"
  capability: "*"
  status: ENABLED
}
--end dump of protolbuffer--
xmpp
--start dump of protolbuffer--
summary_status: ENABLED
config {
  package: "xmpp"
  capability: "*"
  status: ENABLED
}
--end dump of protolbuffer--

使ってみた感想

今試した段階では常にENABLEDが帰って来てるので試せませんが、capabilityサービスから返された状態が「SCHEDULED_FUTURE」「SCHEDULED_NOW」の時の「time_until_scheduled」の値などが気になります。
またcapabilityサービスの実行はおよそ15ms-25ms程度のようです。軽いので、フレームワーク側に組み込んでも良さそうですね。

ついでに書いておく

ついでにAppEngineではPython/Javaどっちかいいんだろう?について。これは色々意見があるし自分もよく聞かれるけど、まだブログでそれについて書いたことが無いのでそろそろ書いておこうかと。
自分の結論としては、どっちでもいい、どっちでも触れるならそんなに違いはない、て事ですね。言語自体に好きなものがあるならそっちを使うのがやっぱり楽しい、と思いますし。
ちなみに、比較対象を深く触った・試した事も無いのに「こっちがイイ、あっちはダメ!」とか言う人がよくいるけど、そういうのは恥ずかしい人だなーと感じる。そういう人はそうやって「イイ」と勧める対象をむしろ貶めているかもしれません。せめて、「こっちはイイけど、あっちは知らないからわからない」ならわかるんだけど、深く触ってもいない試してもいない事に対して「ダメ」といっちゃうのは、典型的な「老害」の行動ですね。年とってからそうなるorもうその立場になってしまってる人なんでしょうね。
また、どんなユーザを対象にしたどんなアプリケーションをどれくらいの規模でどういったメンバで作りたいのか?などの具体的な背景がないとどっちとも言えないのですが、それも考えずに断言しちゃう人もどーにかしてるんでしょかね。

コメントを投稿