リクエストの処理方法

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

サービスを利用するアプリケーションでは、特定のサービスに対するリクエストか、そのサービスの特定のバージョンに対するリクエストを扱うことができます。サービスをアドレス指定する方法の詳細は、リクエストをルーティングする方法をご覧ください。

リクエストの処理

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

App Engine がアプリケーションのウェブ リクエストを受け取ると、アプリケーションの WEB-INF/ ディレクトリにある web.xml ファイルの記述に従って、その URL に対応するサーブレットを呼び出します。App Engine は、Java サーブレット API を使用して、リクエスト データをサーブレットに送信し、レスポンス データを受信します。Java 7 ランタイムで実行されるアプリケーションは、Java サーブレット 2.5 の仕様に基づいていますが、Java 8 ランタイムで実行されるアプリケーションは、Java サーブレット 2.5 または 3.1 の仕様に基づいていることがあります。

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

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

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

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 ランタイム環境は、このサーブレットを中断するために 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 の一般的な原因と推奨されている回避方法については、DeadlineExceededErrors に対処するをご覧ください。

ロギング

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

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

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.");
    // ...
  }
} 

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.");
    // ...
  }
}

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 の App Engine スタンダード環境