お気に入り | 日本語 | ログイン

Java サーブレット環境

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

Java API バージョンの選択

App Engine は、AppCfg ツールを Java SDK から使用してアプリケーションをアップロードするとき、Java ランタイム環境を使用します。

この記事の作成時点では、App Engine Java API には 1 つのバージョンしかありません。この API は SDK に付属のappengine-api-*.jar で表現されます(* は API と SDK のバージョンを示します)。アプリケーションが使用する API のバージョンは、この JAR をアプリケーションの WEB-INF/lib/ ディレクトリに格納することで変更できます。新しいバージョンの Java ランタイム環境がリリースされ、既存のアプリケーションと互換性のない変更が実装された場合、この環境には新しいバージョン番号が付与されます。JAR を新しいバージョン(新しい SDK のもの)と入れ替えてアプリケーションを再アップロードするまで、アプリケーションは以前のバージョンを使用し続けます。

リクエストとサーブレット

App Engine はアプリケーションの Web リクエストを受け取ると、アプリケーションの配備記述子WEB-INF/ ディレクトリの web.xml ファイル)に指定された、URL に対応するサーブレットを呼び出します。Java サーブレット API を使用してサーブレットにリクエスト データを提供し、応答データを受信します。

App Engine は複数の Web サーバーを使用してアプリケーションを実行し、リクエスト処理の信頼性が確保できるようにサーバーの使用数を自動調整します。指定されたリクエストは任意のサーバーに転送できますが、以前に同じユーザーからのリクエストを処理したサーバーと同じサーバーには転送できません。

次の例のサーブレット クラスは、ユーザーのブラウザに簡単なメッセージを表示します。

import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/plain");
        resp.getWriter().println("Hello, world");
    }
}

レスポンス

App Engine はリクエスト オブジェクトと応答オブジェクトを持つサーブレットを呼び出し、サーブレットが応答オブジェクトを作成して返すまで待機します。サーブレットが返されると、応答オブジェクトのデータはユーザーに送信されます。

App Engine はクライアントへデータをまず送信し、アプリケーションで追加の計算を実行して、追加のデータを送信するといった方式のデータ送信はサポートしていません。言い換えると、App Engine は 1 つのリクエストに対してデータの「ストリーミング」はサポートしません。

クライアントがリクエストと一緒に、gzip 圧縮コンテンツを受け入れることができるという内容の HTTP ヘッダーを送ると、App Engine は応答データを自動的に圧縮し、適切な応答ヘッダーを添付します。Accept-Encoding および User-Agent リクエスト ヘッダーを使用して、クライアントが圧縮した応答を受信できるかの信頼性を判断します。カスタム クライアントは、「gzip」の値を使用して Accept-Encoding および User-Agent ヘッダーを両方指定すれば、コンテンツを強制的に圧縮することができます。

リクエスト タイマー

リクエスト ハンドラがリクエストを処理し、レスポンスを返す時間には、制限が設定されています(一般的には 30 秒前後)。制限時間を過ぎると、リクエスト ハンドラは割り込まれます。

Java ランタイム環境は、com.google.apphosting.api.DeadlineExceededException をスローしてサーブレットに割り込みます。リクエスト ハンドラがこの例外をキャッチしない場合、すべてのキャッチされない例外と同様、ランタイム環境は HTTP 500 サーバー エラーをクライアントに返します。

リクエスト ハンドラはこのエラーをキャッチして、応答をカスタマイズできます。ランタイム環境が例外を発生させた後、リクエスト ハンドラは少し時間を使って(1 秒未満)カスタマイズした応答を準備します。

リクエストはレスポンスするまでに最大 30 秒の時間がありますが、App Engine の処理が最適化されているアプリケーションは、数百ミリ秒で処理されるような短いリクエストを実行するアプリケーションです。効率的なアプリケーションはほとんどのリクエストに対して迅速にレスポンスします。レスポンスが遅いアプリケーションは、App Engine のインフラストラクチャでうまくスケーリングを行うことができません。

サンドボックス

App Engine が複数の Web サーバー間のアプリケーションにリクエストを分散できるように、そしてアプリケーション同士が干渉し合わないようにするために、アプリケーションは隔離された「サンドボックス」環境で実行されます。この環境でアプリケーションは、コードの実行、App Engine データストア内のデータの保存とクエリ、App Engine メール、URL フェッチ、ユーザー サービスの使用、ユーザーの Web リクエストの検証とレスポンスの作成を行うことができます。

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

  • ファイルシステムに書き込むこと。アプリケーションで永続的なデータを保存するには、App Engine データストアを使用する必要があります。ファイルシステムからの読み取りはできます。また、アプリケーションを使用してアップロードされたアプリケーション ファイルはすべて使用できます。
  • ソケットを開くことや別のホストに直接アクセスすること。アプリケーションは App Engine URL フェッチ サービスを使用して、ポート 80 とポート 443 から他のホストに HTTP および HTTPS リクエストを行うことができます。
  • サブプロセスやスレッドを生成すること。アプリケーションに対する Web リクエストは、数秒以内に単一プロセスで処理する必要があります。レスポンスに時間がかかりすぎるプロセスは、Web サーバーに負荷がかかりすぎないように終了されます。
  • 他のシステム コールを実行すること。

スレッド

Java アプリケーションは新しい java.lang.ThreadGroup や新しい java.lang.Thread を作成できません。これらの制限はスレッドを使用する JRE クラスにも適用されます。たとえば、アプリケーションは新しい java.util.concurrent.ThreadPoolExecutor または java.util.Timer を作成できません。アプリケーションは、Thread.currentThread().dumpStack() などの現在のスレッドに対して操作を実行できます。

ファイルシステム

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

「リソース ファイル」として扱われるファイルのみが、ファイルシステム経由でアプリケーションにアクセスできます。デフォルトでは、WAR のすべてのファイルが「リソース ファイル」です。appengine-web.xml ファイルを使用して、特定のファイルを除外できます。

java.lang.System

App Engine に適用されない java.lang.System の機能は無効になっています。

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

次の System メソッドは null を返します: inheritedChannel()console()

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

リフレクション

アプリケーションには、それ自身のクラスへの完全で無制限の、リフレクション アクセスが許可されています。プライベート メンバーへのクエリ、java.lang.reflect.AccessibleObject.setAccessible() の使用、プライベート メンバーの読み取り/設定を実行できます。

アプリケーションは、java.lang.String および javax.servlet.http.HttpServletRequest などの JRE および API クラスでリフレクションを行うこともできます。ただし、これらのクラスのパブリック メンバーにのみアクセスでき、保護されたまたはプライベートなメンバーにはアクセスできません。

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

カスタム クラスのロード

App Engine では、カスタム クラスのロードが完全にサポートされています。ただし、App Engine はすべての ClassLoader をオーバーライドして、アプリケーションでロードされたすべてのクラスに同じ権限を割り当てることに注意してください。カスタム クラスのロードを実行する場合は、信頼されないサードパーティ コードのロードに注意してください。

JRE ホワイト リスト

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

ロギング

アプリケーションで、java.util.logging.Logger を使用してアプリケーション ログに情報を書き込めます。アプリケーションのログ データは、管理コンソールを使用して検討、分析したり、appcfg.sh request_logs を使用してダウンロードできます。管理コンソールは、Logger クラスのログ レベルを認識し、異なるレベルのメッセージをインタラクティブに表示します。

サーブレットが標準出力ストリーム(System.out)と標準エラー ストリーム(System.err)に書き込む内容はすべて、App Engine にキャプチャされ、アプリケーション ログに記録されます。標準出力ストリームに書き込まれた行は「INFO」レベルで記録され、標準エラー ストリームに書き込まれた行は「WARNING」レベルで書き込まれます。出力またはエラー ストリームにログを記録するものなら、どのようなロギング フレームワーク(log4j)でも機能します。ただし、管理コンソールのログ レベル表示をより細かく制御するには、ロギング フレームワークで java.util.logging アダプタを使用する必要があります。

import java.util.logging.Logger;
// ...

public class MyServlet extends HttpServlet {
    private static final Logger log = Logger.getLogger(MyServlet.class.getName());

    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {

        log.info("An informational message.");

        log.warning("A warning message.");

        log.severe("An error message.");
    }
}

App Engine Java SDK では、テンプレート logging.properties ファイルが appengine-java-sdk/config/user/ ディレクトリ内に用意されています。これを使用するには、ファイルを WEB-INF/classes ディレクトリ(または WAR 内の任意の場所)にコピーして、システム プロパティ java.util.logging.config.file"WEB-INF/classes/logging.properties"(または、任意に選択した、アプリケーション ルートへの相対パス)にコピーします。システム プロパティを、次のようにして appengine-web.xml ファイルに設定できます。

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    ...

    <system-properties>
        <property name="java.util.logging.config.file" value="WEB-INF/classes/logging.properties" />
    </system-properties>

</appengine-web-app>

Google Plugin for Eclipse の新規プロジェクト ウィザードではこのようなロギング設定ファイルをユーザー向けに作成し、WEB-INF/classes/ に自動的にコピーします。java.util.logging を使用するには、システム プロパティを設定する必要があります。

環境

すべてのシステム プロパティと環境変数は、アプリケーションのみに適用されます。システム プロパティを設定しても、アプリケーションにおける該当プロパティの扱いのみに影響し、JVM における扱いには影響はありません。

アプリケーション用のシステム プロパティと環境変数は配備記述子で設定できます。

App Engine は、アプリケーション サーバーで JVM を初期化するときに次のシステム プロパティを設定します。

  • file.separator
  • path.separator
  • line.separator
  • java.version
  • java.vendor
  • java.vendor.url
  • java.class.version
  • java.specification.version
  • java.specification.vendor
  • java.specification.name
  • java.vm.vendor
  • java.vm.name
  • java.vm.specification.version
  • java.vm.specification.vendor
  • java.vm.specification.name
  • user.dir

割り当てと制限

アプリケーションへの各リクエストはリクエスト割り当てに数えられます。

リクエストの一部として受信したデータは受信した帯域幅(課金対象)の割り当てに数えられます。リクエストへのレスポンスとして送られたデータは送信帯域幅(調整可能)の割り当てに数えられます。

HTTP および HTTPS(セキュア)の両方のリクエストは、リクエスト受信した帯域幅(課金対象)および送信した帯域幅(課金可能)の割り当てに数えられます。管理コンソールの [Quota Details] ページでは、情報としてセキュア リクエスト受信したセキュアな帯域幅送信したセキュアな帯域幅値も別々に表示されます。HTTPS リクエストのみがこの値として数えられます。

リクエスト ハンドラの処理時間として使用された CPU 処理時間はCPU 時間(課金対象)の割り当てに数えられます。

割り当ての詳細については、割り当て、および管理コンソールの「割り当て詳細」セクションをご覧ください。

割り当てに加え、リクエスト ハンドラには次の制限があります。

制限 制限値
リクエスト サイズ 10 MB
レスポンス サイズ 10 MB
リクエスト処理時間 30 秒
同時動的リクエスト 30 *
アプリケーション ファイルの最大数 1,000
静的ファイルの最大数 1,000
アプリケーション ファイルの最大サイズ 10 MB
静的ファイルの最大サイズ 10 MB
アプリケーション ファイルと静的ファイルの最大合計サイズ 150 MB

* アプリケーションは、約 30 個のアクティブな動的リクエストを同時に処理できます。つまり、サーバー側の平均リクエスト処理時間が 75 ミリ秒であるアプリケーションは、待ち時間なく最大で 400 リクエスト/秒処理できます((1000 ms/秒 / 75 ms/リクエスト) * 30 = 400)。 CPU 性能への依存が高いアプリケーションには、長時間実行されるリクエストの処理時に多少の追加待ち時間が発生します。これは、同じサーバーを共有している他のアプリケーションに対して処理時間を割り当てるためです。静的ファイルへのリクエストにはこの制限は適用されません。