リージョン ID
REGION_ID
は、アプリの作成時に選択したリージョンに基づいて Google が割り当てる省略形のコードです。一部のリージョン ID は、一般的に使用されている国や州のコードと類似しているように見える場合がありますが、このコードは国または州に対応するものではありません。2020 年 2 月以降に作成されたアプリの場合、REGION_ID.r
は App Engine の URL に含まれています。この日付より前に作成されたアプリの場合、URL のリージョン ID は省略可能です。
詳しくは、リージョン ID をご覧ください。
このドキュメントでは、App Engine アプリケーションがリクエストを受信してレスポンスを送信する方法を説明します。
詳細については、リクエストのヘッダーとレスポンスのリファレンスをご覧ください。
アプリケーションでサービスを使用している場合は、特定のサービスまたはそのサービスの特定のバージョンへのリクエストを指定できます。サービスのアドレス指定の方法については、リクエストのルーティング方法をご覧ください。
リクエストの処理
アプリケーションは、ウェブサーバーの起動とリクエストの処理を行う役割を果たします。 使用する開発言語に対応している任意のウェブ フレームワークを使用できます。
App Engine はアプリケーションの複数のインスタンスを実行します。各インスタンスには、リクエストを処理するウェブサーバーがそれぞれ割り当てられます。リクエストがルーティングされるインスタンスは任意に決まるため、同じユーザーから連続して送信されたリクエストが同じインスタンスに届くとは限りません。インスタンスは、複数のリクエストを同時に処理できます。インスタンスの数は、トラフィック量の変化に応じて自動的に調整されます。また、app.yaml ファイルの max_concurrent_requests
要素を設定すると、インスタンスが同時に処理できるリクエストの数を変更できます。
app.yaml
構成ファイルに記載されている URL に対応するハンドラ スクリプトを呼び出します。Python 2.7 ランタイムは、下位互換性を維持するため、WSGI 標準と CGI 標準をサポートしますが、WSGI の方をおすすめします。これがないと Python 2.7 の一部の機能が動作しません。アプリケーションのスクリプト ハンドラの構成に応じて、リクエストが WSGI と CGI のどちらを使用して処理されるかが決まります。
次の Python スクリプトはリクエストに対して、HTTP ヘッダーとメッセージ Hello, World!
で応答します。
複数のリクエストを各ウェブサーバーに並列でディスパッチするには、app.yaml
ファイルに threadsafe: true
を追加して、アプリケーションをスレッドセーフとしてマークします。スクリプト ハンドラで CGI が使用されている場合は、同時リクエストを使用できません。
割り当てと制限
App Engine は、トラフィックが増加すると、自動的にアプリケーションにリソースを割り当てます。ただし、次のような制限があります。
App Engine は、1 秒未満でリクエストに応答するレイテンシが短いアプリケーション向けに、自動スケーリングのための容量を予約しています。
また、CPU の制約を大きく受けるアプリケーションでも、同じサーバー上の他のアプリケーションとリソースを効率的に共有するために、追加のレイテンシが生じる場合があります。静的ファイルへのリクエストには、このようなレイテンシの制限は適用されません。
アプリケーションが受信する各リクエストには、リクエスト数の上限が適用されます。リクエストへのレスポンスとして送信されるデータは、[送信帯域幅(課金対象)] の上限の対象としてカウントされます。
HTTP リクエストと HTTPS(セキュア)リクエストのどちらにも、[リクエスト数]、[受信帯域幅(課金対象)]、[送信帯域幅(課金対象)] の上限が適用されます。Google Cloud コンソールの割り当ての詳細ページでは、参考のために、[安全なリクエスト数]、[安全な受信帯域幅]、[安全な送信帯域幅] の値もそれぞれ報告されます。これらの値は、HTTPS リクエストのみに適用されます。詳細については、割り当てページをご覧ください。
リクエスト ハンドラの使用には、それぞれ次の上限や時間制限が適用されます。
上限と時間制限 | 量 |
---|---|
リクエスト サイズ | 32 MB |
レスポンス サイズ | 32 MB |
リクエストのタイムアウト | アプリが使用するスケーリングのタイプに依存 |
最大合計ファイル数(アプリファイルと静的ファイル) | 合計 10,000 ファイル 1 ディレクトリあたり 1,000 ファイル |
アプリケーション ファイルの最大サイズ | 32 MB |
静的ファイルの最大サイズ | 32 MB |
すべてのアプリケーション ファイルと静的ファイルの最大合計サイズ | 最初の 1 GB は無料 最初の 1 GB を超えると、以降は 1 GB あたり毎月 $ 0.026 |
保留中のリクエストのタイムアウト | 10 秒 |
1 つのリクエスト ヘッダー フィールドの最大サイズ | スタンダード環境の第 2 世代ランタイム用に 8 KB。これらのランタイムへのリクエストでヘッダー フィールドが 8 キロバイトを超えると、HTTP 400 エラーが返されます。 |
リクエストに関する上限
すべての HTTP/2 リクエストは、アプリケーション サーバーに転送される際に HTTP/1.1 リクエストに変換されます。
レスポンスに関する上限
動的レスポンスの上限は 32 MB です。スクリプト ハンドラが生成したレスポンスの大きさがこの上限を超える場合は、サーバーから内部サーバーエラー ステータス コード 500 を示す空のレスポンスが返されます。以前の Blobstore または Cloud Storage からのデータを処理するレスポンスには、この上限は適用されません。
第 2 世代ランタイムでは、レスポンス ヘッダーの上限は 8 KB です。この上限を超えるレスポンス ヘッダーは HTTP 502 エラーを返し、ログに
upstream sent too big header while reading response header from upstream
が記録されます。
リクエスト ヘッダー
受信した HTTP リクエストには、クライアントから送信された HTTP ヘッダーが含まれています。セキュリティ上の理由から、一部のヘッダーは、アプリケーションに到達する前に中間プロキシによってサニタイズ(リスクのある部分などを削除)または修正されます。
詳細については、リクエスト ヘッダーのリファレンスをご覧ください。
リクエスト タイムアウトの処理
App Engine はリクエストの存続時間が短いアプリケーション(通常は数百ミリ秒程度)向けに最適化されています。効率的なアプリは、大部分のリクエストに短時間で応答します。そうでないアプリは、App Engine のインフラストラクチャに合わせて適切にスケールされません。このレベルのパフォーマンスを実現するには、システムによって要求される最大リクエスト タイムアウト内に、すべてのアプリがレスポンスを返す必要があります。
アプリがこの期限を超過すると、App Engine はリクエスト ハンドラを中断します。Python ランタイム環境では、google.appengine.runtime
から DeadlineExceededError
例外を発生させることでこの処理を行います。この例外がリクエスト ハンドラでキャッチされないと、すべてのキャッチされない例外と同様に、ランタイム環境からクライアントに HTTP 500 サーバーエラーが返されます。
リクエスト ハンドラは、このエラーをキャッチしてレスポンスをカスタマイズできます。例外発生後、ランタイム環境によりリクエスト ハンドラに対して、カスタム レスポンスを作成するためのわずかな時間(1 秒未満)が与えられます。
2 度目の期限までにハンドラがレスポンスを返さないか、例外が発生しない場合、ハンドラは停止され、デフォルトのエラー レスポンスが返されます。
レスポンス
App Engine は、Request
を使用してハンドラ スクリプトを呼び出し、スクリプトが制御を返すのを待ちます。標準出力ストリームに書き込まれたすべてのデータが HTTP レスポンスとして送信されます。生成するレスポンスにはサイズの上限があり、レスポンスはクライアントに返される前に変更される可能性があります。
詳細については、リクエストに対するレスポンスのリファレンスをご覧ください。レスポンスのストリーミング
App Engine は、レスポンスのストリーミングをサポートしていません。つまり、リクエスト 1 件のデータをチャンクに分けて順に送信することはできません。コードからのデータ全体が前述のように収集されて、単一の HTTP レスポンスとして送信されます。
レスポンスの圧縮
App Engine は、gzip をサポートするクライアントに gzip 圧縮されたコンテンツを可能な限り配信するように設計されています。コンテンツを圧縮する必要があるかどうかを判断するために、App Engine はリクエストを受信すると次の処理を行います。リクエスト内の
Accept-Encoding
ヘッダーとUser-Agent
ヘッダーの両方を確認して、クライアントが圧縮されたレスポンスを適切に受信できるかどうかを確認します。このアプローチにより、gzip 圧縮されたコンテンツを一般的なブラウザで利用する際に発生する、周知のバグを回避します。レスポンス ハンドラに構成した
Content-Type
ヘッダーを表示して、コンテンツを圧縮することが適切であるかどうかを確認します。一般的に、テキストベースのコンテンツ タイプは圧縮に適していますが、バイナリ コンテンツ タイプは適していません。
次の点にご注意ください。
クライアントは
Accept-Encoding
とUser-Agent
の両方のリクエスト ヘッダーをgzip
に設定することにより、テキストベースのコンテンツ タイプの圧縮を強制できます。リクエストで
Accept-Encoding
ヘッダーにgzip
を指定しない場合、App Engine でレスポンス データが圧縮されません。Google フロントエンドは、App Engine の静的ファイルとディレクトリ ハンドラからのレスポンスをキャッシュに保存します。最初にキャッシュに保存されるレスポンス データの種類、レスポンスに指定した
Vary
ヘッダー、リクエストに含まれるヘッダーなど、さまざまな要因によって、クライアントが圧縮データをリクエストしても圧縮されていないデータを受信する場合があります。また、その逆の場合もあります。詳細については、レスポンスのキャッシュ保存をご覧ください。
レスポンスのキャッシュ保存
Google フロントエンド、場合によってはユーザーのブラウザおよびその他の中間キャッシング プロキシ サーバーは、レスポンスに指定した標準キャッシング ヘッダーの指示に従って、アプリのレスポンスをキャッシュに保存します。これらのレスポンス ヘッダーは、フレームワークを介して、またはコード内で直接指定するか、App Engine の静的ファイルとディレクトリ ハンドラを使用して指定できます。
Google フロントエンドでは、キャッシュキーはリクエストの完全な URL です。
静的コンテンツのキャッシュ保存
更新された静的コンテンツの公開後すぐにクライアントが受信できるように、css/v1/styles.css
などのバージョニングされたディレクトリから静的コンテンツを配信することをおすすめします。Google フロントエンドは、キャッシュが期限切れになるまで、キャッシュの検証(更新されたコンテンツの確認)を行いません。キャッシュが期限切れになった後でも、リクエスト URL のコンテンツが変更されるまでキャッシュは更新されません。
app.yaml
で設定できる次のレスポンス ヘッダーは、Google フロントエンドがコンテンツをキャッシュに保存する方法とタイミングに影響します。
Google フロントエンドでコンテンツがキャッシュに保存されるようにするには、
Cache-Control
をpublic
に設定する必要があります。Cache-Control
private
またはno-store
ディレクティブを指定しなければ、Google フロントエンドによってキャッシュに保存されることもあります。app.yaml
でこのヘッダーを設定しない場合、App Engine で静的ファイルまたはディレクトリ ハンドラによって処理されるすべてのレスポンスにこのヘッダーが自動的に追加されます。詳しくは、追加または置換されるヘッダーをご覧ください。Vary
: リクエストで送信されるヘッダーに基づいて、URL に対してさまざまなレスポンスがキャッシュから返されるようにするには、Accept
、Accept-Encoding
、Origin
、X-Origin
のうち 1 つ以上をVary
レスポンス ヘッダーに設定します。カーディナリティが高い可能性があるため、他の
Vary
値についてはデータがキャッシュに保存されません。次に例を示します。
次のレスポンス ヘッダーを指定します。
Vary: Accept-Encoding
アプリは
Accept-Encoding: gzip
ヘッダーを含むリクエストを受信します。App Engine は圧縮されたレスポンスを返し、Google フロントエンドはレスポンス データの gzip 圧縮されたバージョンをキャッシュに保存します。この URL に対するAccept-Encoding: gzip
ヘッダーを含む後続のすべてのリクエストは、キャッシュが無効になる(キャッシュの有効期限が切れた後にコンテンツが変更されたことによる)までキャッシュから gzip 圧縮されたデータを受信します。アプリは、
Accept-Encoding
ヘッダーが含まれていないリクエストを受信します。App Engine は圧縮されていないレスポンスを返し、Google フロントエンドはレスポンス データの圧縮されていないバージョンをキャッシュに保存します。この URL に対するAccept-Encoding
ヘッダーが含まれていない後続のすべてのリクエストは、キャッシュが無効になるまでキャッシュから圧縮されたデータを受信します。
Vary
レスポンス ヘッダーを指定しない場合、Google フロントエンドは URL に対して 1 つのキャッシュ エントリを作成し、作成したエントリをリクエストのヘッダーにかかわらず、すべてのリクエストで使用します。次に例を示します。Vary: Accept-Encoding
レスポンス ヘッダーが指定されていません。- リクエストには
Accept-Encoding: gzip
ヘッダーが含まれ、レスポンス データの gzip 圧縮されたバージョンがキャッシュに保存されます。 - 2 番目のリクエストには
Accept-Encoding: gzip
ヘッダーが含まれていません。ただし、キャッシュにはレスポンス データの gzip 圧縮されたバージョンが含まれているため、クライアントが圧縮されていないデータをリクエストした場合でも、レスポンスは gzip 圧縮されます。
リクエスト内のヘッダーもキャッシュ保存に影響します。
- リクエストに
Authorization
ヘッダーが含まれている場合、コンテンツは Google フロントエンドによってキャッシュに保存されません。
キャッシュの有効期限
デフォルトでは、App Engine の静的ファイルとディレクトリ ハンドラによってレスポンスに追加されるキャッシュ ヘッダーは、クライアントとウェブプロキシ(Google フロントエンドなど)が 10 分後にキャッシュを期限切れにするよう指示します。
任意の有効期限が設定された状態でファイルが転送された場合、一般的にユーザーは自身のブラウザ キャッシュを消去しても、ウェブプロキシのキャッシュからファイルを消去することはできません。アプリの新しいバージョンを再度デプロイしても、キャッシュはリセットされません。静的ファイルを変更する場合には、有効期限は短く(1 時間未満)設定してください。多くの場合、デフォルトの 10 分で十分です。
app.yaml
ファイルで default_expiration
要素を指定すると、すべての静的ファイルとディレクトリ ハンドラのデフォルトの有効期限を変更できます。個別のハンドラに特定の有効期限を設定するには、app.yaml
ファイルのハンドラ要素内で expiration
要素を指定します。
有効期限要素の時間で指定する値は、Cache-Control
と Expires
の HTTP レスポンス ヘッダーの設定に使用されます。
アプリ キャッシング
Python ランタイム環境では、リクエスト間でインポートされたモジュールが単一のウェブサーバーにキャッシュされます。これは、スタンドアロン Python アプリケーションにおいて、モジュールが複数のファイルによってインポートされた場合でも、1 回しか読み込まれないのに似ています。WSGI ハンドラはモジュールのため、リクエスト間でキャッシュされます。CGI ハンドラ スクリプトは、main()
ルーチンを提供する場合のみキャッシュに保存されます。それ以外の場合、CGI ハンドラ スクリプトはリクエストごとに読み込まれます。
アプリ キャッシングにより、レスポンスの時間が大幅に短縮されます。後述するように、すべての CGI ハンドラ スクリプトで main() ルーチンを使用することをおすすめします。
インポートのキャッシュ
効率を考えて、ウェブサーバーは、インポートしたモジュールをメモリに保持し、以降の同じサーバー上の同じアプリケーションに対するリクエストでそれらの再読み込みや再評価を行いません。ほとんどのモジュールは、インポート時にグローバル データを初期化するなどの副次的な影響を及さないため、それらをキャッシュに保存してもアプリケーションの動作は変わりません。
アプリケーションでリクエストのたびに評価対象モジュールに依存するモジュールをインポートする場合は、アプリケーションでこのキャッシング動作に対応する必要があります。
CGI ハンドラのキャッシュ
インポートしたモジュールだけでなく、CGI ハンドラ スクリプト自体をキャッシュに保存するように App Engine に指示できます。ハンドラ スクリプトで main()
という名前の関数を定義すると、インポートしたモジュールと同様に、スクリプトとそのグローバル環境がキャッシュに保存されます。特定のウェブサーバーでスクリプトを初めてリクエストした場合は、通常どおりスクリプトの評価が行われます。以降のリクエストでは、App Engine がキャッシュ環境内の main()
関数を呼び出します。
ハンドラ スクリプトをキャッシュに保存するには、App Engine が引数なしで main()
を呼び出すことができる必要があります。ハンドラ スクリプトが main()
関数を定義しない場合または main()
関数に引数(デフォルトを持たない)が必要な場合は、App Engine がリクエストごとにスクリプト全体を読み込んで評価します。
解析済みの Python コードをメモリに保持することで時間が節約され、迅速なレスポンスが可能になります。グローバル環境のキャッシュには、他にも次の用途があります。
コンパイル済みの正規表現。すべての正規表現が解析され、コンパイル済みの形式で保存されます。コンパイル済みの正規表現をグローバル変数に保存しておくと、アプリ キャッシングを使用して、コンパイル済みのオブジェクトをリクエスト間で再利用できます。
GqlQuery オブジェクト。GqlQuery オブジェクトの作成時に、GQL クエリ文字列が解析されます。パラメータ バインディングを伴う GqlQuery オブジェクトと bind() メソッドの再利用の方が、毎回オブジェクトを再構築するより高速です。値のパラメータ バインディングを伴う GqlQuery オブジェクトをグローバル変数に保存しておくと、リクエストのたびに新しいパラメータ値をバインドすることにより再利用できます。
構成ファイルとデータファイル。アプリケーションがファイルから構成データを読み込んで解析する場合は、解析済みデータをメモリに保持することで、リクエストごとにファイルを再読み込みする必要がなくなります。
ハンドラ スクリプトは、インポート時に main()
を呼び出す必要があります。App Engine は、スクリプトのインポート時に main()
が呼び出されることを想定しているため、サーバーで初めてリクエスト ハンドラを読み込むときに、App Engine がこの関数を呼び出すことはありません。
main()
を使用したアプリ キャッシングにより、CGI ハンドラのレスポンス タイムが大幅に短縮されます。CGI を使用するすべてのアプリケーションでこの方法を採用することをおすすめします。
ロギング
App Engine ウェブサーバーは、ウェブ リクエストへのレスポンスとしてハンドラ スクリプトが標準出力ストリームに書き込むすべての内容をキャプチャします。また、ハンドラ スクリプトが標準エラー ストリームに書き込むすべての内容もキャプチャし、ログデータとして保存します。各リクエストにはrequest_id
が割り当てられます。これは、リクエストの開始時刻に基づく、グローバルで一意の識別子です。アプリケーションのログデータは、Cloud Logging を使用して、Google Cloud コンソールで表示できます。
App Engine Python ランタイム環境には、ログレベル(「debug」、「info」、「warning」、「error」、「critical」)などのロギングのコンセプトを理解するために、Python 標準ライブラリからのロギング モジュールに対する特別なサポートが含まれています。
環境
実行環境は、自動的にいくつかの環境変数を設定します。他の環境変数はapp.yaml
で設定できます。自動的に設定される変数の中には、App Engine 専用のものや、WSGI 標準や CGI 標準に含まれるものがあります。Python コードは、os.environ
ディクショナリを使用して、これらの変数にアクセスできます。
次の環境変数は App Engine に固有のものです。
CURRENT_VERSION_ID
: 「X.Y」のような現在実行中のアプリケーションのメジャー バージョンとマイナー バージョン。メジャー バージョン番号(X)は、アプリのapp.yaml
ファイルに指定されています。マイナー バージョン番号(Y)は、アプリの各バージョンが App Engine にアップロードされたときに自動的に設定されます。開発用ウェブサーバーでは、マイナー バージョンは常に「1」です。AUTH_DOMAIN
: Users API でユーザーを認証するために使用されるドメイン。appspot.com 上でホストされているアプリにはgmail.com
のAUTH_DOMAIN
が割り当てられ、すべての Google アカウントを受け入れます。カスタム ドメイン上でホストされているアプリには、カスタム ドメインと同じAUTH_DOMAIN
が割り当てられます。INSTANCE_ID
: リクエストを処理するフロントエンド インスタンスのインスタンス ID が含まれています。この ID は 16 進文字列(00c61b117c7f7fd0ce9e1325a04b8f0df30deaaf
など)です。ログインしている管理者は、URLhttps://INSTANCE_ID-dot-VERSION_ID-dot-SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com
でこの ID を使用できます。リクエストはこの特定のフロントエンド インスタンスにルーティングされます。リクエストを処理できない場合、そのインスタンスはすぐに 503 を返します。
次の環境変数は WSGI 標準と CGI 標準に含まれており、App Engine では特殊な動作をします。
SERVER_SOFTWARE
: 開発用ウェブサーバーでは、この値は「Development/X.Y」です。ここで、「X.Y」はランタイムのバージョンです。App Engine 上で動作している場合、この値は「Google App Engine/X.Y.Z」です。
他の環境変数は、WSGI 標準や CGI 標準に従って設定されます。これらの変数の詳細については、必要に応じて WSGI 標準または CGI 標準をご覧ください。
app.yaml
ファイルで環境変数を設定することもできます。
env_variables:
DJANGO_SETTINGS_MODULE: 'myapp.settings'
次の webapp2 リクエスト ハンドラは、アプリケーションが認識できるすべての環境変数をブラウザに表示します。
リクエスト ID
リクエストのときに、そのリクエストに固有のリクエスト ID を保存できます。リクエスト ID は、Cloud Logging でそのリクエストをログから検索する場合に使用できます。
次のサンプルコードは、リクエストのコンテキストでリクエスト ID を取得する方法を示しています。
HTTPS 接続の強制
セキュリティ上の理由から、すべてのアプリケーションは、https
で接続するようクライアントに促すべきです。特定のページまたはドメイン全体で http
よりも https
を優先するようにブラウザに指示するには、レスポンスに Strict-Transport-Security
ヘッダーを設定します。次に例を示します。
Strict-Transport-Security: max-age=31536000; includeSubDomains
コードから生成されるレスポンスに対してこのヘッダーを設定するには、flask-talisman
ライブラリを使用します。
非同期バックグラウンド作業の処理
バックグラウンド作業とは、HTTP レスポンスを配信した後、アプリがリクエストに対して行う作業です。バックグラウンド作業をアプリ内で実施することは避け、コードを見直して、レスポンスを配信する前にすべての非同期オペレーションが完了するようにしてください。
長時間実行ジョブには、Cloud Tasks の使用をおすすめします。Cloud Tasks では、HTTP リクエストが長時間継続し、非同期処理が終了した後にのみレスポンスを返します。