Java 8 用 Remote API

Java SDK には Remote API というライブラリが含まれており、これを使用することで、いずれの Java アプリケーションからも App Engine サービスに透過的にアクセスできます。たとえば、Remote API を使用して、ローカルマシンで実行しているアプリから本番環境のデータストアにアクセスすることが可能です。また、Remote API を使用すると、特定の App Engine アプリのデータストアに別の App Engine アプリからアクセスすることもできます。

サーバーに Remote API を構成する

Remote API のサーバー コンポーネントは、Java 用の App Engine ランタイムに含まれる Java サーブレットです。このサーブレットは、Remote API クライアントからのリクエストを受信して適切なバックエンド サービスにディスパッチし、サービス呼び出しの結果をクライアントに返します。Remote API サーブレットをインストールするには、web.xml に次の内容を追加します。

<servlet>
  <display-name>Remote API Servlet</display-name>
  <servlet-name>RemoteApiServlet</servlet-name>
  <servlet-class>com.google.apphosting.utils.remoteapi.RemoteApiServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>RemoteApiServlet</servlet-name>
  <url-pattern>/remote_api</url-pattern>
</servlet-mapping>

認証済みユーザーが存在しないか、認証済みユーザーがアプリケーションの管理者でない場合、サーブレットはエラーを返します。したがって、追加のセキュリティ構成は不要です。上記の設定でアプリをデプロイした後は、Remote API クライアントをインストールしたあらゆるアプリがそのサービスを使用できます。これには、Python Remote API を使用している Python クライアントも含まれます。

スタンドアロン クライアントに Remote API を構成する

Remote API のクライアント コンポーネントを Java アプリケーションで使用できるように構成するには、まずクラスパスに ${SDK_ROOT}/lib/impl/appengine-api.jar${SDK_ROOT}/lib/appengine-remote-api.jar を追加します。次に、コードで Remote API を構成してインストールします。

import com.google.appengine.tools.remoteapi.RemoteApiInstaller;
import com.google.appengine.tools.remoteapi.RemoteApiOptions;

// ...
RemoteApiOptions options = new RemoteApiOptions()
    .server("your_app_id.appspot.com", 443)
    .useApplicationDefaultCredential();

RemoteApiInstaller installer = new RemoteApiInstaller();
installer.install(options);
// ... all API calls executed remotely
installer.uninstall();

Remote API クライアントは、OAuth 2.0 を使用するアプリケーションのデフォルト認証情報に依存します。

認証情報を取得するには、次のコマンドを実行します。

gcloud auth application-default login

開発用サーバーでローカルに実行されている App Engine アプリにも、同じように簡単に接続できます。

RemoteApiOptions options = new RemoteApiOptions()
    .server("localhost", 8888) // server name must equal "localhost"
    .useDevelopmentServerCredential();

実行時にエンティティをデータストアに挿入する Java アプリケーション全体を次に示します。

public class RemoteApiExample {

  public static void main(String[] args) throws IOException {
    String serverString = args[0];
    RemoteApiOptions options;
    if (serverString.equals("localhost")) {
      options = new RemoteApiOptions().server(serverString,
        8080).useDevelopmentServerCredential();
    } else {
      options = new RemoteApiOptions().server(serverString,
        443).useApplicationDefaultCredential();
    }
    RemoteApiInstaller installer = new RemoteApiInstaller();
    installer.install(options);
    try {
      DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
      System.out.println("Key of new entity is " + ds.put(new Entity("Hello Remote API!")));
    } finally {
      installer.uninstall();
    }
  }
}

App Engine クライアントに Remote API を構成する

Remote API を使用すると、特定の App Engine アプリケーションのサービスに別の App Engine アプリケーションからアクセスすることもできます。そのためには、スタンドアロン Java クライアントの場合と同様に、${SDK_ROOT}/lib/appengine-remote-api.jarWEB-INF/lib ディレクトリに追加してから、クライアント App Engine アプリで Remote API を構成してインストールする必要があります。

RemoteApiInstaller は、インストールを実行するスレッドのみで Remote API をインストールするため、このクラスのインスタンスを複数のスレッド間で共有しないように注意してください。

Maven で Remote API を使用する

Maven プロジェクトで Remote API 機能を使用するには、プロジェクトの pom.xml ファイルに次の依存性を追加します。

<dependency>
  <groupId>com.google.appengine</groupId>
  <artifactId>appengine-remote-api</artifactId>
  <version>1.9.64</version>
</dependency>
<dependency>
  <groupId>com.google.appengine</groupId>
  <artifactId>appengine-api-1.0-sdk</artifactId>
  <version>1.9.64</version>
</dependency>

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

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

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

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

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

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

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

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

クエリに対する反復処理を避ける

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

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

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

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

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

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