Webリクエストの処理

昨日の続きです。

アプリケーションの実行

フロントエンドサーバは (静的ファイルでなく) リソースファイルにマッピングされたURLへのリクエストを検出すると、アプリケーションサーバの一台にリクエストを転送します。以前も書いたように、HTTPリクエストが同じクライアント (ブラウザ) から来たものであっても同じアプリケーションサーバにリクエストが転送されるわけではありません。その時々で異なるアプリケーションサーバに転送されます。当然ですが、デバッグサーバは一つのサーバなので、そのようなことはありません。


リクエストを受けたアプリケーションサーバは以下のリソースをランタイム環境にロードします。

  • Pythonスクリプトファイル
  • リソースファイル (HTMLテンプレートとかいろいろ)
  • 設定


設定って何?管理コンソールで設定出来る項目のことだと思います。こちらはおいおいってことで。

ところでこれまでよくランタイム環境という言葉が出てきましたが、これって一体何のこと?と疑問が湧いてきます。以下のものがランタイム環境に含まれるとのこと。


私はこんな感じにイメージしています (間違っているかも知れません) 。ランタイム環境はちょっとしたPC (CPUとメモリがありますよね) です。OSはインタープリタに当たります。ファイルシステムもあればライブラリ (API) もあります。この内CPUやメモリのスペックなどはお金を積めばパワーアップ出来る。下手に仮想環境とかって理解すると私のようなトーシロは混乱してしまいますので。。。

このお金を積めばパワーアップ出来るということをスケールすると言います。厳密にはお金を積まなくてもスケールしていますが。

ランタイム環境

↑は私の勝手な理解ですのであしからず。ランタイム環境に関して、もう少し細かく見ていきます。

Google Codeのドキュメントを見ると見かけるCGIアプリケーションサーバとはPythonスクリプトはこのCGIと呼ばれるインターフェイスを使ってやりとり (HTTPリクエストを渡して、HTTPレスポンスを返す) をします*1。このCGIを自分で実装しないといけないとなると、CGIについて知らないといけませんが、幸いApp Engineでは様々なフレームワークを使うことで、CGIについて知らなくてもアプリケーションを実装することが出来ます。

なお、スクリプト実行前にアプリケーションサーバは以下の変数を設定します。もちろんスクリプト内で見ることが可能です。

  • APPLICATION_ID
  • CURRENT_VERSION_ID
  • AUTH_DOMAIN
  • SERVER_SOFTWARE


この内、使うだろうなぁと思うのはCURRENT_VERSION_IDぐらいですかね。ていうか昨日使いましたね。

サンドボックス

何度も出てきているキーワード、サンドボックスサンドボックスは日本語で砂場。子供が砂場から出ないように親は気をつけているものですが、App Engineのサンドボックスも同じ意味合いです。アプリケーションサーバは一つのスクリプトだけを実行しているわけではありません。同時に複数のスクリプトを実行しています。これらのスクリプトが実行されるランタイム環境は違うわけですが、スクリプトがこのランタイム環境を越えて他のスクリプトに影響を与えないようにしているので、サンドボックスというわけです。具体的にはランタイム環境に次のような制約を課すことでサンドボックスを実現しています。

  1. アプリケーション内部でプロセスやスレッドは起動出来ない
  2. 外部のサーバと通信することは出来ない
  3. ファイルシステムは読み込み専用
  4. 他のランタイム環境を除くことは出来ない


1に関しては代わりにTaskQueueサービスを使うことが出来ます。2に関してはURLFetchサービスが使えます。こうやって考えるとTaskQueueやURLFetchサービスの存在は必然的であることが分かります。つまりないと困ります。今までは何も考えずにこれらのサービスを使っていましたが、こうやって考えるとまた見方も変わってきます。

アプリケーションキャッシング

何度も書いているようにApp Engineでは同じHTTPリクエストが同じアプリケーションサーバ上で実行されるとは限りません。ですが、毎回毎回スクリプトをロードしていたら処理速度が低下し、ユーザの待ち時間が無視出来ないレベルになってしまいます。なのでApp Engineにはアプリケーションキャッシュと呼ばれる機構があります。


※ここからの解釈はちょっと自信がありません (間違ってるかも知れません) 。


先に作成したアプリケーションで、以下のようなコードを書きました。Pythonの定型句です。

application = webapp.WSGIApplication([('/', MainPange)], debug=True)

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()


ここで要チェックポイントは二つあります。一つ目はapplicationというグローバル変数です。App EngineではHTTPリクエスト毎にランタイム環境が異なる可能性があるので、このグローバル変数はHTTPリクエスト毎に異なるランタイム環境上のメモリに割り当てられる可能性があります。しかし一度メモリ上に割り当てられて、そのランタイム環境が削除されない限りは残っています。つまりキャッシュされているのです。フロントエンドサーバはそういうところを狙って配信するんでしょうかね?そっちの方が明らかに効率がいいですからね。ですが、このキャッシュを実現するには条件があります。それが二つ目のポイントですが、次の箇所です。

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()


アプリケーションサーバはmain()という関数があると、その他の部分は一度読み込んだらキャッシュし、以降はmain()関数しか呼ばなくなります。ただし最初の読み込み時はそうは行かないので (main()関数は呼んでくれないので) 、自ら呼び出すようにしています。ちなみにmain()関数がないと毎回スクリプト全体が評価されます。main()関数を利用しない手はないですよね。

ロギング

App Engineではログを出力することが可能です。このログは管理コンソールからチェックすることが出来ます。

↑で書いたアプリケーションキャッシュを確認するためにこんな感じのログを仕込んでみました。

# ...

application = webapp.WSGIApplication([('/', MainPage)], debug = True)

# アプリケーションがキャッシュされるかチェック
logging.info('This message is in global area.')

def main():
    # 毎回呼ばれるかチェック
    logging.info('This message is in main function.')
    run_wsgi_app(application)

if __name__ == '__main__':
    main()


実際にログを確認してみると、上記ログは最初にアクセスした時しか出力されませんでした。たしかにキャッシュされています。



※黄色枠が初回アクセス時のログで、水色枠がリロード時のログです。

クォータと制限

この辺り、私のように適当に勉強していると全然頭に入って来ないんです。まだこの本読み始めたばかりですが、ほんと買ってよかったです。だからと言って理解出来たわけではありませんけどね。


まずクォータという用語。Google Codeのドキュメントを読んでいるとよく見かけるキーワードですが、クォータは一日の始まり (太平洋標準時の深夜) にリセットされるリソース制限のことを指します。「今自分のアプリはどんな感じかなぁ」と思ったら、管理コンソールのQuota Detailsセクションに現時点での状況を見ることが出来ます。

リクエストの制限

リクエストに関する制限で絶対に押さえておかないといけないのが、リクエストを受け手からレスポンスを返すまでの時間です。ずばり30秒です。30秒近く経過すると例外が発生します。それでも処理を終了出来ないとプロセスが終了してしまいます。

CPUの制限

現時点でのCPU時間の制限は (無料利用で) 6.50 CPU hoursとなっています。これはクォータですので、1日で総計6.5時間CPUを使うことが出来る、ということです。んん?どういうこと??と思い、いろいろ調べた結果の私の理解はこんな感じです。


まずここで言うところの「CPU」ですが、1.2GHzのIntel x86プロセッサを想定しているそうです。いまではちょっと物足りないスペックですね。このCPUを一日で6.5時間まではタダで使えるということです。それ以上使おうとすると例外が発生します。んで重要なのが、このCPUを必要な時に連続して使えるわけではない、ということ。あくまで総計なのです。細切れなのです。なぜかというと、一つのアプリケーションサーバ上では複数のスクリプトが動作しています。あるスクリプトが多くのCPUを消費すると他のスクリプトがCPUを利用する機会が少なくなってしまいます。そういう場合、アプリケーションサーバはCPUを多く消費するスクリプトへのCPU配分を制限します。つまりあまりCPUが回らないようになります。OSのタイムシェアリングシステムのイメージですね。なので連続してCPUが使える場合に比べて、レスポンスを返すまでの時間が長くなってしまいます。


そう考えると商用サイトをApp Engineで運用する場合、少なくとも24時間フルで動けるようにしないとちょっと厳しいですね。ていうか同時に何人ものユーザがアクセスすることを考えるとそれ以上になります。この時App Engineのスケール出来る特徴が生きてくるわけです。アクセス数が多くなってきたらお金を払って制限を広げていくことが出来るのです。改めて考えてみると、面白い仕組みですね。

サービスの制限

ランタイム環境内で利用可能なサービスにも各種制限があります。たとえばメール送信数とか、画像変換回数など。この中でも一番気にしないといけないのが、サービス呼び出し、レスポンスでやりとりするデータのサイズです。これは全てのサービスで1MBに制限されています。

デプロイメントの制限

デプロイメントに関しては以下の二つの制約が重要です。

  • デプロイするファイルは最大10MB
  • デプロイするファイルは最大3000


ただしファイル数が3000を越える場合はzipでアーカイブしてしまう方法で対応可能です。とりあえず現段階ではこの制限に達することはまずないので、詳細は割愛。

課金対象のクォータ

現段階ではお金を払ってクォータの制限を広げることは考えていないので、詳細は割愛しますが、面白いのは予算を設定するけど、請求される金額は無料クォータ制限を越えた分だけ、というところです。使った分だけ払えばいい、というのはとてもうれしいですよね。余分に払いすぎた〜とかないので。

リソース利用状況ヘッダ

App Engineが返すHTTPレスポンスのヘッダにリソース利用状況が付加されています。


要素 説明
X-AppEngine-Estimated-CPM-US-Dolloars このリクエストを1000回繰り替えした時に予想される料金
X-AppEngine-Resource-Usage リソースの利用状況


この情報だけでも有用なので、この情報を取得するためだけの管理者用サイトを作るのもよさげです。なお、このヘッダは開発者アカウントでログインしていないと付与されないので注意が必要。

*1:ちなみにJavaServlet、Goは ...?