Java 8 Runtime Environment

App Engine を使用すると、Google のスケーラブルなインフラストラクチャとサービスを使用するウェブ アプリケーションをビルドできます。 App Engine は、Java 8 JVM を使用して、ウェブ アプリケーションを実行します。App Engine は、アプリのサーブレット クラスを呼び出して、この環境でリクエストの処理とレスポンスの準備を行います。

App Engine プラットフォームには、コードから呼び出せる多数の組み込み API サービスが用意されています。また、アプリケーションでは、指定の間隔で実行されるタスクのスケジュールを構成できます。

アプリ用の Java 8 ランタイムの指定

アプリに Java 8 ランタイムを使用するには、次の行を appengine-web.xml ファイルに追加します。

<runtime>java8</runtime>

このように変更するだけで、以前 Java 7 ランタイムを使用していた既存の App Engine アプリケーションは、Java 8 ランタイムで実行されるようになります。

App Engine API for Java は、Cloud SDK の一部として App Engine SDK に含まれた appengine-api-*.jar で表されます(* は API と App Engine SDK のバージョンを表します)。

アプリケーションで使用する API のバージョンを選択するには、この JAR をアプリケーションの WEB-INF/lib/ ディレクトリに配置するか、Maven を使用して依存関係を処理します。リリースされた新しいバージョンの Java Runtime Environment に、既存のアプリケーションと互換性のない変更が導入された場合、この環境には新しいバージョン番号が付与されます。

Maven を使用して依存関係を処理する

Maven を使用してすべての依存関係を管理できます。たとえば、以下の pom.xml エントリには、Maven Central で入手可能な最新の App Engine API(Rappengine-api-1.0-sdk)が含まれています。

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

サンドボックス

App Engine Java ランタイムは、複数のアプリケーションへのリクエストを複数のウェブサーバーに分散し、アプリケーションが相互に干渉しないようにします。 App Engine アプリのレスポンスが遅くならないようにする必要があります。アプリケーションへのウェブ リクエストは、リクエストのタイムアウト制限以内に処理される必要があります。このレスポンス時間の制限を超えるプロセスは、ウェブサーバーのオーバーロードを回避するために終了します。

ユーザーがファイルを書き込むことができる場所は /tmp ディレクトリのみです。 /tmp 内のファイルは、インスタンスに割り当てられたメモリを消費します。この場所に格納されているファイルは、このインスタンスでのみ使用でき、この特定のインスタンスの存続期間中のみ使用できます。

アプリケーションがリソース ファイルを取得する通常の方法は、WEB-INF の下でアプリケーションで依存するファイルをパッケージ化し、Class.getResource()ServletContext.getResource()、または同様の方法でアプリから読み込むことです。デフォルトでは、WAR に含まれるすべてのファイルは「リソース ファイル」です。このセットからファイルを除外するには、appengine-web.xml ファイルを使用します。

クラスローダーでの JAR の順序付け

クラス名の衝突を解決するために、JAR ファイルをスキャンしてクラスを検索する順序を定義し直す必要が生じることがあります。このような場合は、appengine-web.xml ファイルに <priority-specifier> 要素を含む <class-loader-config> 要素を追加して、特定の JAR ファイルが優先的に読み込まれるようにできます。例:

<class-loader-config>
  <priority-specifier filename="mailapi.jar"/>
</class-loader-config>

このようにすると、war/WEB-INF/classes/ ディレクトリ内の JAR ファイルではなく「mailapi.jar」が、クラスを検索する最初の JAR ファイルになります。

複数の JAR ファイルが優先されている場合は、元の読み込み順序(互いに対する順序)が使用されます。つまり、<priority-specifier> 要素自体の順序は関係ありません。

スレッド

Java 8 ランタイムでは、App Engine の ThreadManager API と Java の組み込み API(new Thread() など)を使用して、スレッドを作成できます。現在、App Engine API(com.google.appengine.api.*)を呼び出す場合は、リクエスト スレッドまたは ThreadManager API を使用して作成されたスレッドからそれらの API を呼び出す必要があります。

アプリケーションでは次のことが可能です。

currentRequestThreadFactory()ThreadPoolExecutor を作成する場合、サーブレット リクエストが完了する前に shutdown() を明示的に呼び出す必要があります。これを行わないと、リクエストが完了せず、最終的にアプリケーション サーバーで障害が発生します。 一部のライブラリで ThreadPoolExecutor が作成される場合があるので注意してください。

アプリケーションは、thread.interrupt() など、現在のスレッドに対するオペレーションを実行できます。

各リクエストは、50 の同時実行 App Engine API リクエスト スレッドに制限されます。

スレッドを使用する場合は、ExecutorRunnable などの高レベル同時実行オブジェクトを使用します。これらのオブジェクトは、割り込みスケジュールと記帳のような、細かいながらも重要な同時実行の詳細の多くを処理します。

App Engine API で作成される同時実行バックグランド スレッドの最大数はインスタンスごとに 10 個です (この上限は App Engine API とは関係のない、通常の Java スレッドには適用されません)。

ツール

サポートされている IDE

Cloud Tools for Eclipse を導入すると、Eclipse IDE に、App Engine プロジェクト用の新しいプロジェクト ウィザードとデバッグ構成が追加されます。App Engine プロジェクトを Eclipse 内からライブで本番環境にデプロイできます。

Cloud Tools for IntelliJ を使用すると、IntelliJ IDEA 内で App Engine アプリケーションを実行してデバッグできます。また、IDE を閉じることなく、App Engine プロジェクトをライブで本番環境にデプロイできます。

サポートされているビルドツール

開発プロセスを高速化するには、Apache Maven または Gradle 用の App Engine プラグインを使用できます。

ローカルの開発用サーバー

開発用サーバーを使用すると、アプリケーションの開発とテストのために、ローカルのコンピュータ上でアプリケーションを実行できます。サーバーは Datastore サービスをシミュレートします。また、開発用サーバーでは、テスト中にアプリケーションが実行するクエリに基づいて、Datastore インデックス構成を生成できます。

AppCfg

AppCfg は、スタンドアロンの App Engine SDK for Java に含まれています。これは、App Engine で動作しているアプリケーションのコマンドライン操作を処理する多目的ツールです。AppCfg を使用すると、アプリケーションを App Engine にアップロードすることも、データストア インデックス構成のみを更新することもできるため、コードを更新する前に新しいインデックスを作成できます。また、アプリのログデータをダウンロードすることもできるため、独自のツールでアプリのパフォーマンスを分析できます。

同時実行とレイテンシ

トラフィックに対処するために必要なインスタンスの数に特に大きく影響するのはアプリケーションのレイテンシです。リクエストを短時間のうちに処理できるサービスであれば、1 つのインスタンスで多くのリクエストを処理できます。

シングル スレッドのインスタンスでは、同時に 1 件のリクエストしか処理できません。そのため、レイテンシと、インスタンスで処理できる 1 秒あたりのリクエスト数の間には直接的な相関関係があります。たとえば、10 ミリ秒のレイテンシでは、1 つのインスタンスで 1 秒間に 100 件のリクエストを処理します。

マルチスレッドのインスタンスでは、多数のリクエストの同時処理が可能です。そのため、CPU 使用量と 1 秒あたりのリクエスト処理数の間に直接の相関関係があります。

Java アプリは、同時リクエストをサポートしているため、1 つのインスタンスで他のリクエストが完了するまで待機している間に、新しいリクエストを処理できます。同時実行性を使用すると、アプリが必要とするインスタンスの数が大幅に少なくなりますが、アプリをマルチスレッド対応として設計する必要があります。

たとえば、B4 インスタンス(約 2.4 GHz)がリクエスト 1 件につき 10 M サイクルを使用する場合、1 つのインスタンスでの 1 秒あたりのリクエスト処理数は 240 件になります。リクエスト 1 件につき 100 M サイクルを使用する場合は、1 つのインスタンスでの 1 秒あたりのリクエスト処理数は 24 件になります。このような数字は理想的な条件を想定したものですが、インスタンスの処理能力という点ではかなり現実的な数字です。