Java 7 ランタイム環境

警告: Java 7 ランタイムは非推奨となっています。Google では、利用規約に従って Java 7 ランタイムを引き続きサポートいたしますが、できるだけ早く Java 8 ランタイムに移行してください。

App Engine を使用すると、Google のスケーラブルなインフラストラクチャとサービスを使用するウェブ アプリケーションをビルドできます。

はじめに

App Engine は、Java 7 JVM を使用して、安全な「サンドボックス」環境で Java ウェブ アプリケーションを実行します。App Engine は、アプリケーションのサーブレット クラスを呼び出して、この環境でリクエストの処理とレスポンスの準備を行います。

App Engine Java 7 ランタイムは OpenJDK 7 に基づいており、ウェブ アプリケーション用に Java Servlet 2.5 標準を使用します。ユーザーは、アプリケーションのサーブレット クラス、JavaServer Pages(JSP)、静的ファイル、データファイルを、デプロイ記述子(web.xml ファイル)などの構成ファイルとともに標準 WAR ディレクトリ構造で用意します。App Engine は、デプロイ記述子に従ってサーブレットを呼び出すことでリクエストを処理します。

安全な「サンドボックス」環境は、サービスとセキュリティ上の理由から、作成したアプリケーションを隔離します。サンドボックス環境では、アプリケーションは、他のアプリケーションのパフォーマンスとスケーラビリティに影響しない処理しか実行できないようになります。たとえば、スレッドを生成したり、ローカル ファイル システムにデータを書き込んだり、任意でネットワークに接続したりすることはできません。また、JNI などのネイティブ コードを使用することもできません。JVM は、サンドボックスの制限内で動作するすべての Java バイトコードを実行できます。

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

Java Guestbook アプリのチュートリアルでは、Java のテクノロジーと App Engine を使用したウェブ アプリケーション開発についてわかりやすく解説しています。

Java ランタイムの選択

App Engine API for Java は、App Engine SDK 付属の appengine-api-*.jar で表されます(* は API と App Engine SDK のバージョンを表します)。アプリケーションで使用する API のバージョンを選択するには、この JAR をアプリケーションの WEB-INF/lib/ ディレクトリに配置するか、Maven を使用して依存関係を処理します。リリースされた新しいバージョンの Java ランタイム環境に、既存のアプリケーションと互換性のない変更が導入された場合、この環境には新しいバージョン番号が付与されます。新しいバージョンの JAR(新しい App Engine SDK のもの)に置き替えてアプリケーションを再アップロードするまで、アプリケーションは以前のバージョンを使用し続けます。

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

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

<dependency>
    <groupId>com.google.appengine</groupId>
    <artifactId>appengine-api-1.0-sdk</artifactId>
    <version><span class="notranslate exporttrans-dynamic">print setvar.appengine_java_version</span></version>
</dependency>

サンドボックス

App Engine が複数のアプリケーションへのリクエストを複数のウェブサーバーに分散できるようにして、アプリケーションが相互に干渉しないようにするために、アプリケーションは制限された「サンドボックス」環境で実行されます。この環境では、アプリケーションはコードの実行、Cloud Datastore 内でのデータの保存とクエリ、App Engine メール、URL 取得、ユーザー サービスの使用、ユーザーのウェブ リクエストの確認とレスポンスの準備が可能です。

App Engine アプリケーションでは、次のことはできません。

  • ファイルシステムへの書き込み。アプリケーションは、データを永続的に保存するために、Cloud Datastore を使用する必要があります。ファイルシステムからの読み取りは可能です。また、アプリケーションでアップロードされたアプリケーション ファイルはすべて利用可能です。

  • 遅いレスポンス。アプリケーションへのウェブ リクエストは、数秒以内に処理される必要があります。レスポンスまでに非常時に長い時間がかかるプロセスは、ウェブサーバーの過負荷を防ぐために終了します。

  • その他の種類のシステムコールの実行。

ファイル システム

Java アプリケーションでは、ファイルシステムへの書き込みに使用される java.io.FileWriter などのクラスは使用できません。ただし、java.io.FileReader などのクラスを使用して、ファイル システムから独自のファイルを読み取ることはできます。また、Class.getResource()ServletContext.getResource() などを使用して、「リソース」である独自のファイルにアクセスすることもできます。

アプリケーションは、「リソース ファイル」とみなされるファイルにのみ、ファイル システムを通じてアクセスできます。デフォルトでは、WAR に含まれるファイルはすべて「リソース ファイル」です。「リソース ファイル」のグループからファイルを除外するには、appengine-web.xml ファイルを使用します。

java.lang.System

java.lang.System クラスの機能のうち、App Engine に適用されない機能は無効にされます。

System メソッドのうち、exit()gc()runFinalization()runFinalizersOnExit() は、App Engine では機能しません。

System メソッドのうち、inheritedChannel()console()null を返します。

アプリケーションでネイティブ JNI コードを提供したり、直接呼び出したりすることはできません。次の System メソッドは java.lang.SecurityException を生成します。load()loadLibrary()setSecurityManager()

リフレクション

アプリケーションは、自身のクラスに対して制限のない完全なリフレクション アクセスが許可されています。

そのため、private メンバーに対するクエリ、java.lang.reflect.AccessibleObject.setAccessible() メソッドの呼び出し、private メンバーの読み取りや設定を行うことができます。

また、アプリケーションは、java.lang.Stringjavax.servlet.http.HttpServletRequest などの JRE クラスと API クラスでリフレクションを使用することもできます。ただし、アクセスできるのはこれらのクラスの public メンバーのみで、protected メンバーや private メンバーにはアクセスできません。

アプリケーションは、自身に属していないクラスでリフレクションを使用することはできません。setAccessible() メソッドを使用してこのような制限を回避することもできません。

カスタムクラスの読み込み

App Engine では、カスタムクラスの読み込みが完全にサポートされています。アプリケーションは、アプリケーション固有のクラス読み込みロジックを実装する独自の ClassLoader サブクラスを定義できます。ただし、App Engine はすべての ClassLoader をオーバーライドし、アプリケーションで読み込まれたすべてのクラスに同じ権限を割り当てるので注意してください。カスタムクラスの読み込みを実行する場合、信頼できないサードパーティのコードを読み込む際は十分に気を付けてください。

クラスローダーでの 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> 要素自体の順序は関係ありません。

JRE ホワイトリスト

Java 標準ライブラリ(Java ランタイム環境、JRE)内のクラスへのアクセスは、App Engine JRE ホワイトリスト内のクラスに限定されます。

署名付き JAR ファイルには非対応

App Engine のプリコンパイルは、署名付き JAR ファイルとの互換性がありません。アプリケーションがプリコンパイルされている場合(デフォルト)、署名付き JAR ファイルは読み込めません。アプリケーションが署名付き JAR を読み込もうとすると、ランタイムの App Engine は次のような例外を生成します。

java.lang.SecurityException: SHA1 digest error for com/example/SomeClass.class
    at com.google.appengine.runtime.Request.process-d36f818a24b8cf1d(Request.java)
    at sun.security.util.ManifestEntryVerifier.verify(ManifestEntryVerifier.java:210)
    at java.util.jar.JarVerifier.processEntry(JarVerifier.java:218)
    at java.util.jar.JarVerifier.update(JarVerifier.java:205)
    at java.util.jar.JarVerifier$VerifierStream.read(JarVerifier.java:428)
    at sun.misc.Resource.getBytes(Resource.java:124)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:273)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:266)

これを回避するには、2 とおりの方法があります。

スレッド

Java アプリケーションでは、新しいスレッドを作成できますが、その作成方法には、いくつかの制限があります。作成したスレッドは、作成元のリクエストよりも「長く存続」することはできません。

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

ただし、スレッドの作成には ThreadManager のメソッドのいずれかを使用する必要があります。new Thread() を直接呼び出すことや、デフォルトのスレッド ファクトリを使用することはできません。

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

各リクエストの同時リクエスト スレッド数は 50 までに制限されています。1 つのリクエストで 50 個を超えるスレッドを作成しようとすると、Java ランタイムによって java.lang.IllegalStateException がスローされます。

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

バックグラウンド スレッド

手動または基本のスケーリング インスタンスで実行するコードは、バックグラウンド スレッドを開始できます。このスレッドは、スレッドを生成したリクエストよりも長く存続できます。これにより、インスタンスは任意の周期またはスケジュールによってタスクを実行できるようなるほか、リクエストがユーザーに返された後でもバックグラウンドで作業を続行できるようになります。

Java の同時実行は、デバッグが困難な問題につながることがあります。スレッドの使用を避けるには、タスクキュータスクのスケジュールPub/Sub の使用を検討してください。

バックグラウンド スレッドのロギング エンティティは、スレッドを生成するエンティティとは無関係です。バックグラウンド スレッドについて詳しくは、App Engine の ThreadManager に関するドキュメントをご覧ください。

import com.google.appengine.api.ThreadManager;
import java.util.concurrent.atomic.AtomicLong;

AtomicLong counter = new AtomicLong();

Thread thread = ThreadManager.createBackgroundThread(new Runnable() {
  public void run() {
    try {
      while (true) {
        counter.incrementAndGet();
        Thread.sleep(10);
      }
    } catch (InterruptedException ex) {
      throw new RuntimeException("Interrupted in loop:", ex);
    }
  }
});
thread.start();

自動スケーリング サービスで実行するコードで、バックグラウンド スレッドを開始しようとすると、例外が発生します。

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 プラグインを使用できます。

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

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

AppCfg

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

同時実行とレイテンシ

トラフィックに対処するために必要なインスタンスの数に特に大きく影響するのはアプリケーションのレイテンシです。リクエストを短時間のうちに処理できるサービスであれば、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 件になります。このような数字は理想的な条件を想定したものですが、インスタンスの処理能力という点ではかなり現実的な数字です。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Java の App Engine スタンダード環境