リクエストの処理方法

このドキュメントでは、App Engine アプリケーションがリクエストを受信してレスポンスを送信する方法を説明します。詳細については、リクエストのヘッダーとレスポンスのリファレンスをご覧ください。

アプリケーションでサービスが使用されている場合は、特定のサービスまたはそのサービスの特定のバージョンへのリクエストを指定できます。サービスのアドレス指定の方法について詳しくは、リクエストのルーティング方法をご覧ください。

リクエストの処理

アプリケーションは、ウェブサーバーの起動とリクエストの処理を行う役割を果たします。使用する開発言語に対応している任意の Web フレームワークを使用できます。

App Engine がアプリケーションのウェブ リクエストを受け取ると、アプリケーションの WEB-INF/ ディレクトリにある web.xml ファイルの記述に従って、その URL に対応するサーブレットを呼び出します。また、Java Servlet 2.5 または 3.1 API 仕様をサポートして、リクエスト データをサーブレットに渡し、レスポンス データを受け取ります。

App Engine はアプリケーションの複数のインスタンスを実行します。各インスタンスには、リクエストを処理する独自のウェブサーバーが割り当てられます。リクエストがルーティングされるインスタンスは任意に決まるため、同じユーザーから連続して送信されたリクエストが同じインスタンスに届くとは限りません。インスタンスの数は、トラフィックの変化に応じて自動的に調整されます。

デフォルトでは、各ウェブサーバーは一度に 1 つのリクエストのみを処理します。複数のリクエストを各ウェブサーバーに並列でディスパッチするには、<threadsafe>true</threadsafe> 要素を appengine-web.xml ファイルに追加して、アプリケーションをスレッドセーフとしてマークします。

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

Java 8

// With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required.
@WebServlet(name = "requests", description = "Requests: Trivial request", urlPatterns = "/requests")
public class RequestsServlet extends HttpServlet {

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

Java 7

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

割り当てと制限

App Engine は、トラフィックが増加すると、自動的にアプリケーションにリソースを割り当てます。ただし、次のような制限があります。

  • App Engine は、1 秒未満でリクエストに応答するレイテンシが短いアプリケーション向けに、自動スケーリングのための容量を予約しています。多くのリクエストで 1 件につき 1 秒を超えるなど、レイテンシが大きく、スループットが高いアプリケーションでは、シルバー、ゴールド、またはプラチナのサポートが必要です。これらのレベルのサポートを契約済みの場合は、サポート担当者に連絡してスループットの上限を引き上げることができます。

  • また、CPU の制約を大きく受けるアプリケーションでも、同じサーバー上の他のアプリケーションとリソースを効率的に共有するために、追加のレイテンシが生じる場合があります。静的ファイルへのリクエストには、このようなレイテンシの制限は適用されません。

アプリケーションが受信する各リクエストは、[リクエスト数] の上限対象としてカウントされます。リクエストへのレスポンスとして送信されるデータは、[送信帯域幅(課金対象)] の上限対象としてカウントされます。

HTTP リクエストと HTTPS(セキュア)リクエストのどちらにも、[リクエスト数]、[受信帯域幅(課金対象)]、[送信帯域幅(課金対象)] の上限が適用されます。GCP Console の割り当ての詳細ページでは、参考のために、[安全なリクエスト数]、[安全な受信帯域幅]、[安全な送信帯域幅] の値もそれぞれ報告されます。これらの値としてカウントされるのは、HTTPS リクエストのみです。詳細については、割り当てページをご覧ください。

リクエスト ハンドラの使用には、具体的には次の制限が適用されます。

制限
リクエスト サイズ 32 MB
レスポンス サイズ 32 MB
リクエスト期間 60 秒
最大合計ファイル数(アプリファイルと静的ファイル) 合計 10,000 ファイル
1 ディレクトリあたり 1,000 ファイル
アプリケーション ファイルの最大サイズ 32 MB
静的ファイルの最大サイズ 32 MB
すべてのアプリケーション ファイルと静的ファイルの最大合計サイズ 最初の 1 GB は無料
最初の 1 GB を超えると、以降は 1 GB あたり毎月 $ 0.026

レスポンスに関する制限事項

動的レスポンスの上限は 32 MB です。スクリプト ハンドラが生成したレスポンスの大きさがこの制限を超える場合は、サーバーから内部サーバーエラー ステータス コード 500 を示す空のレスポンスが返されます。Blobstore または Google Cloud Storage からのデータを処理するレスポンスには、この上限は適用されません。

リクエスト ヘッダー

受信した HTTP リクエストには、クライアントから送信された HTTP ヘッダーが含まれています。セキュリティ上の理由から、一部のヘッダーは、アプリケーションに到達する前に中間プロキシによってサニタイズ(リスクのある部分などを削除)または修正されます。

詳細については、リクエスト ヘッダーのリファレンスをご覧ください。

リクエストのレスポンス

App Engine はリクエスト オブジェクトとレスポンス オブジェクトを使ってサーブレットを呼び出した後、サーブレットがレスポンス オブジェクトに値を設定して返すのを待機します。サーブレットが戻ると、レスポンス オブジェクトのデータがユーザーに送信されます。

生成するレスポンスには制限があり、レスポンスはクライアントに返される前に変更される可能性があります。

詳細については、リクエストに対するレスポンスのリファレンスをご覧ください。

レスポンスのストリーミング

App Engine は、レスポンスのストリーミングをサポートしていません。つまり、リクエスト 1 件のデータをチャンクに分けて順に送信することはできません。コードからのデータ全体が前述のように収集されて、単一の HTTP レスポンスとして送信されます。

レスポンスの圧縮

クライアントから送信された HTTP ヘッダーに記述された元のリクエストから、そのクライアントが圧縮(gzip 圧縮)コンテンツを受け入れ可能であることがわかると、App Engine はハンドラのレスポンス データを自動的に圧縮し、適切なレスポンス ヘッダーを付加します。圧縮されたレスポンスをクライアントが適切に受信できるかどうかを、Accept-EncodingUser-Agent の両方のリクエスト ヘッダーを使用して判断します。

カスタム クライアントが圧縮されたレスポンスを受信できることを示すには、Accept-EncodingUser-Agent の両方のヘッダーに値 gzip を指定します。レスポンスの Content-Type も、圧縮が適切かどうかを判断するために使用されます。一般に、テキストベースのコンテンツ タイプは圧縮されますが、バイナリ コンテンツ タイプは圧縮されません。

レスポンスが App Engine で自動的に圧縮された場合は、Content-Encoding ヘッダーがレスポンスに追加されます。

リクエスト期限の指定

リクエスト ハンドラがリクエストに対するレスポンスを生成して返すまでの時間には一定の制限があり、通常は約 60 秒です。この期限に達すると、リクエスト ハンドラは中断されます。Java Runtime Environment は、このサーブレットを中断するために com.google.apphosting.api.DeadlineExceededException をスローします。この例外をキャッチするリクエスト ハンドラがない場合は、ランタイム環境から HTTP 500 サーバーエラーがクライアントに返されます。

リクエスト ハンドラが存在していて DeadlineExceededException がキャッチされた場合は、ランタイム環境はリクエスト ハンドラがカスタム レスポンスを作成するまで待ちます(1 秒未満)。例外が発生した後、リクエスト ハンドラがカスタム レスポンスを作成するのに要する時間が 1 秒を超えた場合は、HardDeadlineExceededError が生成されます。

DeadlineExceededExceptionsHardDeadlineExceededErrors のどちらの場合も、リクエストが強制終了され、インスタンスが削除されます。

アプリケーションでこの期限までの残り時間を調べるには、com.google.apphosting.api.ApiProxy をインポートして ApiProxy.getCurrentEnvironment().getRemainingMillis() を呼び出します。これが役立つのは、アプリケーションで予定している作業に長時間かかる可能性がある場合です。たとえば、ある作業の処理に 5 秒かかる場合に getRemainingMillis() から返される時間がそれよりも短いときは、その作業を開始しても意味がありません。

リクエスト 1 件の処理に 60 秒かかる場合もありますが、App Engine はリクエストの存続時間が短いアプリケーション向け(通常は数百ミリ秒程度)に最適化されています。効率的なアプリは、大部分のリクエストに短時間で応答します。そうでないアプリは、App Engine のインフラストラクチャに合わせて適切にスケールされません。

DeadlineExceededError の一般的な原因と推奨される回避方法については、DeadlineExceededError の処理をご覧ください。

ロギング

アプリケーションの情報をアプリケーション ログに書き込むには、java.util.logging.Logger を使用します。アプリケーションのログデータは、Stackdriver Logging を使用すれば GCP Console 上に表示できます。ログに記録された各リクエストには、リクエスト ID が割り当てられます。これは、リクエストの開始時間に基づく、グローバルで一意の識別子です。GCP Console では、Logger クラスのログレベルが認識され、さまざまなレベルのメッセージを対話形式で表示できます。

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

Java 8

// With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required.
@WebServlet(
    name = "RequestLogging",
    description = "Requests: Logging example",
    urlPatterns = "/requests/log"
)
public class LoggingServlet extends HttpServlet {

  private static final Logger log = Logger.getLogger(LoggingServlet.class.getName());

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    log.info("An informational message.");
    log.warning("A warning message.");
    log.severe("An error message.");
    // ...
  }
}

Java 7

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

  @Override
  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/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/logging.properties" />
    </system-properties>

</appengine-web-app>

サーブレットは、INFO ログレベル(log.info())を使用してメッセージをログに書き込みます。デフォルトのログレベルは WARNING で、その場合は INFO メッセージは出力されません。ログレベルを変更するには、logging.properties ファイルを編集します。ログレベルを設定する方法の具体例については、ゲストブック フォーム アプリケーションをご覧ください。

環境

すべてのシステム プロパティと環境変数は、開発したアプリケーションにのみ適用されます。あるシステム プロパティを設定したときに、その影響を受けるのはそのアプリケーションから見たそのプロパティの値だけであり、JVM から見た値は変化しません。

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

App Engine は、ランタイム環境を表すために次のようなシステム プロパティを設定します。

  • com.google.appengine.runtime.environment は、App Engine で実行する場合は "Production"、開発用サーバーで実行する場合は "Development" です。

    System.getProperty() を使用する方法以外にも、システム プロパティには type-safe API を使用してアクセスできます。次に例を示します。

    if (SystemProperty.environment.value() ==
        SystemProperty.Environment.Value.Production) {
        // The app is running on App Engine...
    }
    
  • com.google.appengine.runtime.version は、ランタイム環境のバージョン ID です(たとえば "1.3.0")。バージョンを取得するには、String version = SystemProperty.version.get(); を実行します。

  • com.google.appengine.application.id は、アプリケーションの ID です。アプリケーションの ID を取得するには、String ID = SystemProperty.applicationId.get(); を実行します。

  • com.google.appengine.application.version は、現在実行しているアプリケーション サービスのメジャー バージョンとマイナー バージョンです(「X.Y」の形式)。メジャー バージョン番号(X)はサービスの appengine-web.xml ファイルで指定されます。マイナー バージョン番号("Y")は、アプリの各バージョンが App Engine にアップロードされたときに自動的に設定されます。この ID を取得するには、String ID = SystemProperty.applicationVersion.get(); を実行します。

    開発用ウェブサーバー上では、返されるメジャー バージョンは常にデフォルト サービスのバージョンであり、マイナー バージョンは常に「1」です。

次のシステム プロパティも、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

インスタンス ID

リクエストを処理するインスタンスの ID を取得するには、次のコードを使用します。

com.google.apphosting.api.ApiProxy.getCurrentEnvironment().getAttributes().get("com.google.appengine.instance.id")

本番環境の場合、管理者はログインしていれば、ID を URL に http://[INSTANCE_ID].myApp.appspot.com/ の形式で使用できます。リクエストは指定したインスタンスにルーティングされます。リクエストを処理できない場合、そのインスタンスはすぐに 503 を返します。

リクエスト ID

リクエストのときに、そのリクエストに固有のリクエスト ID を保存できます。リクエスト ID は、後でリクエストとそのリクエストのログを関連付けるのに使用できます。

次のコードは、リクエストのコンテキストでリクエスト ID を取得する方法を示しています。

com.google.apphosting.api.ApiProxy.getCurrentEnvironment().getAttributes().get("com.google.appengine.runtime.request_log_id")

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

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

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