内部のApp Engine API

このトピックで、Nick Johnsonのブログの一連の翻訳を開きたいと思います。 Nickは、GAEに関する非常に有用な記事を公開し、彼の経験を共有し、異常な実験を行います。 これらの資料がお役に立てば幸いです。



App Engineを単純なアプリケーションにのみ使用する場合は、これ以上読むことを控えることをお勧めします。 低レベルの最適化に興味がある場合、またはApp Engineの最も親密なコンポーネントで動作するライブラリを作成する場合は、先に進んでください!



共通のAPI





最終的に、各API呼び出しは、4つの引数を持つ1つの共通インターフェースを通過します。サービスの名前(たとえば、「datastore_v3」または「memcache」)、メソッドの名前(例えば、「Get」または「RunQuery」)、要求および応答。 要求と応答はプロトコルバッファであり、プロセス間で構造化データを交換するためにGoogleで広く使用されているバイナリ形式です。 プロトコルバッファの特定のタイプの要求と応答は、呼び出されるメソッドによって異なります。 API呼び出しが行われると、要求で送信されたデータから要求プロトコルバッファーが形成され、応答プロトコルバッファーは空のままになり、API呼び出しの応答によって返されたデータでさらにいっぱいになります。



API呼び出しは、上記の4つのパラメーターを 'dispatch'関数に渡すことによって行われます。 Pythonでは、 apiproxy_stub_mapモジュールがこの役割を実行します。 このモジュールは、サービス名(説明されている最初のパラメーター)とそれを処理するスタブとの対応を維持する役割を果たします。 SDKでは、このコンプライアンスはローカルスタブ(APIの動作を模倣するモジュール)の作成により保証されます。 本番環境では、実際のAPIへのインターフェースは、アプリケーションの起動時にこのモジュールに渡されます。 アプリケーションコードが読み込まれる前でも。 API呼び出しを行うプログラムは、API自体の実装を気にするべきではありません。 彼女は、コールがどのように処理されるかを知りません。ローカルに、またはシリアル化されて別のマシンに送信されました。



ディスパッチ関数は、呼び出されたAPIに適切なスタブを見つけるとすぐに、呼び出しを送信します。 将来的に何が起こるかは、APIと環境に完全に依存しますが、本番環境では全体として次のことが起こります:プロトコルバッファリクエストはバイナリデータにシリアル化され、このAPIを処理するサーバーに送信されます。 たとえば、リポジトリへの呼び出しはシリアル化され、リポジトリサービスに送信されます。 このサービスは、要求を逆シリアル化し、実行し、応答オブジェクトを作成し、シリアル化し、呼び出しを行ったスタブに送信します。 最後に、スタブはバッファープロトコル応答への応答を逆シリアル化し、値を返します。



各API呼び出しでバッファプロトコルレスポンスを処理する必要がある理由を疑問に思う必要があります。 これは、プロトコルバッファ形式では、送信されるデータのタイプを区別する方法が提供されないためです。 受信する予定のメッセージの構造を知っていることを前提としています。 したがって、応答をデシリアライズする方法を理解する「コンテナ」を提供する必要があります。



低レベルのストレージリクエストを実行して、キー名でエンティティインスタンスを取得することにより、これらすべてがどのように機能するかの例を見てみましょう。

  1. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  2. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  3. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  4. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  5. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  6. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  7. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  8. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  9. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  10. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  11. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)



  12. from google.appengine.datastore import datastore_pb from google.appengine.api import apiproxy_stub_map def do_get(): request = datastore_pb.GetRequest() key = request.add_key() key.set_app(os.environ['APPLICATION_ID']) pathel = key.mutable_path().add_element() pathel.set_type('TestKind') pathel.set_name('test') response = datastore_pb.GetResponse() apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', request, response) return str(response)





非常に徹底的でしょ? 特に、同様の高レベルのメソッド-TestKind.get_by_key_name( 'test')と比較して! アクションのシーケンス全体を理解する必要があります:プロトコルバッファーの要求と応答の形成、関連情報(この場合はエンティティ名とキー名)での要求の入力、apiproxy_stub_map.MakeSyncCallの呼び出しによるリモートオブジェクト(RPC)の作成。 通話が終了すると、回答が入力されます。これは、文字列表示で確認できます。

  1. エンティティ{
  2. エンティティ<
  3. キー<
  4. アプリ:「deferredtest」
  5. パス<
  6. 要素{
  7. タイプ:「TestKind」
  8. 名前:「テスト」
  9. }
  10. >
  11. >
  12. entity_group <
  13. 要素{
  14. タイプ:「TestKind」
  15. 名前:「テスト」
  16. }
  17. >
  18. プロパティ<
  19. 名前:「テスト」
  20. 値<
  21. stringValue: "foo"
  22. >
  23. 複数:false
  24. >
  25. >
  26. }


各APIの各リモート呼び出しは、内部的に同じパターンを使用します-要求オブジェクトと応答オブジェクトのパラメーターセットのみが区別されます。



非同期呼び出し



上記のプロセスは、同期API呼び出しを参照しています。つまり、さらに処理を行う前に、応答を待っています。 ただし、App Engineプラットフォームは非同期API呼び出しをサポートしています。 非同期リクエストでは、スタブに呼び出しを送信し、スタブは応答を待たずに即座に戻ります。 その後、後で回答を要求する(または必要に応じて待機する)か、回答を受信したときに自動的に呼び出されるコールバック関数を設定します。



この記事の執筆時点では、非同期呼び出しをサポートしているのは一部のAPIのみです。特にURLフェッチAPIは 、複数のWebリソースを並行して取得するのに非常に役立ちます。 非同期APIの動作原理は、通常のAPIの動作原理と同じです。非同期呼び出しがライブラリに実装されているかどうかに依存します。 urlfetchのようなAPIは非同期操作に適合していますが、他のより複雑なAPIは非同期に動作するのがはるかに困難です。

同期呼び出しを非同期呼び出しに変換する方法の例を見てみましょう。 前の例との違いは太字で強調されています。

  1. google.appengine.datastoreからimport datastore_pb
  2. google.appengine.apiからimport apiproxy_stub_map
  3. google.appengine.apiインポートデータストアから
  4. def do_ async _get():
  5. リクエスト= datastore_pb.GetRequest()
  6. キー= request.add_key()
  7. key.set_app(os.environ ['APPLICATION_ID'])
  8. pathel = key.mutable_path()。add_element()
  9. pathel.set_type( 'TestKind')
  10. pathel.set_name( 'test')
  11. 応答= datastore_pb.GetResponse()
  12. rpc = datastore.CreateRPC()

  13. rpc.make_call( 'Get'、リクエスト、レスポンス)
  14. 応答RPC、応答




違いは、リポジトリへの特定の呼び出しに対してRPCオブジェクトを作成し、MakeSyncCall()の代わりにそのmake_call()メソッドを呼び出すことです。 次に、オブジェクトとプロトコルバッファの応答を返します。



これは非同期呼び出しであるため、RPCオブジェクトを返したときに完了していません。 非同期応答を処理する方法はいくつかあります。 たとえば、コールバック関数をCreateRPC()メソッドに渡すか、RPCオブジェクトの.check_success()メソッドを呼び出して、呼び出しが完了するのを待つことができます。 実装が簡単なので、最後のオプションを示します。 関数の簡単な例を次に示します。

  1. TestKind(key_name = 'test'、test = 'foo')。Put()
  2. self.response.headers ['Content-Type'] = 'text / plain'
  3. rpc、response = do_async_get()
  4. self.response.out.write( "RPCステータスは%s \ n"%rpc.stateです)
  5. rpc.check_success()
  6. self.response.out.write( "RPCステータスは%s \ n"%rpc.stateです)
  7. self.response.out.write(str(応答))


出力データ:

  1. RPCステータスは1です
  2. RPCステータスは2
  3. エンティティ{
  4. エンティティ<
  5. キー<
  6. アプリ:「deferredtest」
  7. パス<
  8. 要素{
  9. タイプ:「TestKind」
  10. 名前:「テスト」
  11. }
  12. >
  13. >
  14. entity_group <
  15. 要素{
  16. タイプ:「TestKind」
  17. 名前:「テスト」
  18. }
  19. >
  20. プロパティ<
  21. 名前:「テスト」
  22. 値<
  23. stringValue: "foo"
  24. >
  25. 複数:false
  26. >
  27. >
  28. }


ステータス定数はgoogle.appengine.api.apiproxy_rpcモジュールで定義されます。この場合、1は「実行中」、2は「完了」を意味します。つまり、RPCは実際に非同期で実行されます。 もちろん、このクエリの実際の結果は、通常の同期クエリの結果と同じです。



低レベルのRPCがどのように機能し、非同期呼び出しを行う方法がわかったので、プログラマーとしての可能性は大きく広がりました。 TwistedのようなApp Engine APIへの新しい非同期インターフェースを最初に作成するのは誰ですか?



All Articles