Remote API で App Engine にアクセスする

リージョン ID

REGION_ID は、アプリの作成時に選択したリージョンに基づいて Google が割り当てる省略形のコードです。一部のリージョン ID は、一般的に使用されている国や州のコードと類似しているように見える場合がありますが、このコードは国または州に対応するものではありません。2020 年 2 月以降に作成されたアプリの場合、REGION_ID.r は App Engine の URL に含まれています。この日付より前に作成されたアプリの場合、URL のリージョン ID は省略可能です。

詳しくは、リージョン ID をご覧ください。

Remote API ライブラリを使用すると、App Engine アプリケーションで使用可能なサービスに Python クライアントからアクセスできます。

たとえば、App Engine アプリケーションで Datastore または Google Cloud Storage を使用している場合、Python クライアントは Remote API を使用してこれらのストレージ リソースにアクセスできます。

また、Remote API を使用すると、ローカルマシンで実行されているアプリやローカルの Remote API インタラクティブ シェルからアプリケーションのデータストアにアクセスできます。Remote API は実際のサービスを利用するので、このアクセスでは、割り当て制限や課金対象のリソースが使用されます。

アプリで Remote API アクセスを有効にする

アプリケーションで Remote API を有効にする最も簡単な方法は、デフォルト URL /_ah/remote_api/ を指定するアプリの app.yaml ファイルで builtins ディレクティブを使用することです。同じファイルで url ディレクティブを使用すると、別の URL を指定できます。

builtin

app.yaml ファイルの builtins ディレクティブを指定すると、デフォルトの URL /_ah/remote_apiで Remote API を使用できます。

runtime: python27
api_version: 1
threadsafe: true

builtins:
- remote_api: on

URL

app.yamlurl ディレクティブを使用すると、Remote API に別の URL を指定できます。

- url: /some-URL/*
  script: google.appengine.ext.remote_api.handler.application

この変更を行う前に、アプリケーションを App Engine にデプロイしてください。

Remote API シェルを使用する

Python SDK には Remote API シェルが含まれています。このシェルを使用すると、アプリケーションで使われている App Engine サービスで Python コマンドを実行できます。App Engine にアプリをアップロードしたときに使用された認証情報が自動的に使用されるので、認証情報を入力する必要はありません。

Remote API シェルを開始するには:

  1. ローカルマシンのターミナル ウィンドウで次のコマンドを実行します。

    SDK-INSTALL-DIRECTORY/remote_api_shell.py -s YOUR-PROJECT-ID. REGION_ID.r.appspot.com

    [SDK-INSTALL-DIRECTORY] を Python 用 App Engine SDK へのパスに置き換え、[YOUR-PROJECT-ID] をプロジェクト ID に置き換えます。

  2. 表示されたインタラクティブ シェルで、必要な Python コマンドを実行します。たとえば、アプリケーションで Datastore を使用している場合、10 個のレコードを取得するには、次の ndb クエリを実行します。

     >>> from google.appengine.ext import ndb
     >>>
     >>> # Fetch 10 keys from the datastore
     >>> ndb.Query().fetch(10, keys_only=True)
    

ローカル クライアントで Remote API を使用する

ローカル アプリケーションで Remote API を使用すると、App Engine で実行されるアプリが使用しているサービスにもアクセスできます。

ローカル アプリケーションで Remote API を使用するには:

  1. Remote API を有効にします

  2. 次のように、Python ディレクトリ PYTHONPATH 環境変数をエクスポートします。

     export PYTHONPATH=/usr/somedir/v3/bin/python2.7
    

    このパスを Python の実際の場所で置き換えます。

  3. Python 用 App Engine SDK の場所を PYTHONPATH に追加します。

     export GAE_SDK_ROOT="/usr/local/home/mydir/google_appengine"
     export PYTHONPATH=${GAE_SDK_ROOT}:${PYTHONPATH}
    

    上の SDK パスを App Engine SDK の実際のパスで置き換えます。

  4. App Engine SDK モジュールすべてが正しくインポートされるように、クライアント コードで dev_appserver をインポートし、dev_appserver.fix_sys_path() を呼び出します。

    try:
        import dev_appserver
        dev_appserver.fix_sys_path()
  5. 次の remote_api_stub コードをアプリケーションに追加し、プロジェクト ID をコードに渡すようにします。

    remote_api_stub.ConfigureRemoteApiForOAuth(
        '{}.appspot.com'.format(project_id),
        '/_ah/remote_api')

    Remote API のデフォルト URL /_ah/remote_api を使用しない場合は、使用する URL を反映させるために上記のコードを変更する必要があります。remote_api_stub.ConfigureRemoteApiForOAuth の定義とドキュメントについては、SDK ファイル [SDK-INSTALL-DIRECTORY]/google/appengine/ext/remote_api/remote_api_stub.py をご覧ください。

  6. App Engine サービスにアクセスできるように、必要な App Engine インポートと Python コードを追加します。次のサンプルコードでは、プロジェクトのデータストアにアクセスします。

    
    import argparse
    
    try:
        import dev_appserver
        dev_appserver.fix_sys_path()
    except ImportError:
        print('Please make sure the App Engine SDK is in your PYTHONPATH.')
        raise
    
    from google.appengine.ext import ndb
    from google.appengine.ext.remote_api import remote_api_stub
    
    def main(project_id):
        remote_api_stub.ConfigureRemoteApiForOAuth(
            '{}.appspot.com'.format(project_id),
            '/_ah/remote_api')
    
        # List the first 10 keys in the datastore.
        keys = ndb.Query().fetch(10, keys_only=True)
    
        for key in keys:
            print(key)
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser(
            description=__doc__,
            formatter_class=argparse.RawDescriptionHelpFormatter)
        parser.add_argument('project_id', help='Your Project ID.')
    
        args = parser.parse_args()
    
        main(args.project_id)
  7. アプリケーションを App Engine にデプロイして、Remote API クライアントを開始します。

     python your-client.py YOUR-PROJECT-ID
    

    your-client.py はクライアント モジュールに、YOUR-PROJECT-ID はプロジェクト ID に置き換えます。これは client.py コードサンプルのように、クライアントがプロジェクト ID をコマンドライン入力として受け取ることを前提としています。

制限事項とベスト プラクティス

remote_api モジュールでは、できる限りネイティブの App Engine データストアと同じ動作をするように最大限の努力が払われています。このため、効率が犠牲になる場合があります。remote_api を使用する際には、以下の点に留意してください。

どのデータストア リクエストにもラウンドトリップがある

HTTP を使ってデータストアにアクセスするため、ローカルなアクセスより若干オーバーヘッドとレイテンシが増えます。高速化と負荷軽減のため、get と put をバッチ処理することや、クエリからエンティティをバッチで取得することにより、ラウンドトリップの回数を減らすようにしてください。これは、remote_api に限らず、データストアの通常の使用方法でも役に立ちます。バッチ オペレーションは単一の Datastore オペレーションと見なされるためです。たとえば、次のコマンドがあるとします。

for key in keys:
  rec = key.get()
  rec.foo = bar
  rec.put()

上記のコマンドの代わりに、次のコマンドを使用できます。

records = ndb.get_multi(keys)
for rec in records:
  rec.foo = bar
  ndb.put_multi(records)

いずれのコマンドも同じ結果になりますが、後者に必要となるラウンドトリップは合計で 2 回だけです。前者の場合、エンティティごとに 2 回のラウンドトリップが必要になります。

remote_api へのリクエストは割り当てを消費する

remote_api では HTTP を使用するため、データストアの API を呼び出すたびに、データストアに対する通常の割り当てに加えて、HTTP リクエストと入出力バイト数の割り当て使用量を消費します。remote_api を使って一括更新を行っている場合には、この点に注意してください。

API での 1 MB の制限が適用される

API のリクエストとレスポンスに対する 1 MB の制限は、ネイティブに実行している場合と同じように適用されます。エンティティが特に大きい場合は、この制限に達しないように、一度に取得または put する数を制限する必要があります。残念ながら、これはラウンドトリップを減らすことと矛盾します。このため、リクエストまたはレスポンスのサイズ制限を超えない範囲で、できるだけ大きなバッチを使用することをおすすめします。ただし、これが問題となる可能性のあるエンティティは多くありません。

クエリの反復処理を避ける

データストアのアクセスで通常使用されるパターンの一例は次のとおりです。

q = MyModel.query()
for entity in q:
  # Do something with entity

このようにアクセスする場合、SDK はデータストアから 20 個単位のバッチでエンティティを取得し、取得したエンティティを使い果たすたびに新しいバッチを取得します。各バッチは remote_api による個別のリクエストで取得する必要がありますが、これでは効率的とは言えません。そのため、remote_api は各バッチに対してまったく新しいクエリを実行し、オフセット機能を使用して結果セットの途中からエンティティを取得します。

必要なエンティティ数がわかる場合は、その個数を指定することにより、1 回のリクエストですべてのエンティティを取得できます。

entities = MyModel.query().fetch(100)
for entity in entities:
  # Do something with entity

必要なエンティティ数がわからない場合は、カーソルを使用して大きな結果セットに対する反復処理を効率的に実行できます。また、これによって通常のデータストア クエリに設定されている 1,000 エンティティの制限を回避することもできます。

トランザクションは効率が悪い

remote_api を使ったトランザクションの実装では、トランザクション中に取得されたエンティティに関する情報と、トランザクション中に put または delete されたエンティティのコピーを蓄積します。トランザクションが commit されると、この情報のすべてが App Engine サーバーに渡されます。そこでは、トランザクションで使用されたすべてのエンティティが再度取得され、変更がないことを確認します。そして、トランザクションが行ったすべての変更を put と delete で実行し、commit します。競合がある場合、サーバーはトランザクションをロールバックしてクライアント側に通知します。クライアント側はこの手順をもう一度最初から繰り返す必要があります。

このアプローチは正しく動作し、機能的にはローカルのデータストアでのトランザクションとまったく同じですが、かなり非効率です。必要なところには必ずトランザクションを使用しつつ、効率の観点から、実行するトランザクションの数と複雑さを減らすようにしてください。