AppEngineでスピンアップが遅くなり過ぎないようにとか考えると、Pythonでもモジュールの遅延ロードをしないといけないわけで、まあ書く。
app.yaml
この例ではすべてのリクエストをmain.pyで受ける。
application: nullpobug-sandbox version: 1 runtime: python api_version: 1 handlers: - url: .* script: main.py
main.py
WSGIアプリケーションの形にしてます。PATH_INFOの値で実行するアプリケーションを切り替えてます。
# coding: utf-8 import sys from google.appengine.ext.webapp import util def load_app(name): """ モジュールからアプリケーション関数をロードする関数 """ bits = name.split('.') module_name = '.'.join(bits[:-1]) app_name = bits[-1] # モジュールをインポート(ロード) __import__(module_name, {}, {}, []) module = sys.modules[module_name] # モジュールから属性名で参照して返す return getattr(module, app_name) # キャッシュ用変数 apps = {} def get_app(name): """ キャッシュからアプリケーション関数を返す キャッシュになければモジュールからロードする """ global apps # ロード済みか判定 if name in apps: app = apps[name] else: app = load_app(name) # キャッシュする apps[name] = app return app def entrypoint(environ, start_response): path = environ['PATH_INFO'] # URLで実行するアプリケーションを切り替える if path == '/': app = get_app('app1.application') elif path == '/app2/': app = get_app('app2.application') else: # マッチしないので404 start_response('404 NotFound', [('Content-Type', 'text/plain')]) return '404 NotFound' return app(environ, start_response) def main(): util.run_wsgi_app(entrypoint) if __name__ == '__main__': main()
app1.py
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return 'app1.application'
app2.py
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return 'app2.application'
これで、 "/" にアクセスすると "app1.application" と表示され、 "/app2/" でアクセスすると "app2.application" と表示される。マッチしない場合は404になる。
まあwerkzeugとかdjangoなどのフレームワーク、モジュールだと同様のコードを持ってたりしますがね。
warmup使えばそんなの気にしなくていいぜ!とかあるかもしれませんが、コード量が多くてロードが遅すぎると結局タイムアウトしてしまったりするので、その対策としても使える。
追記
そういえばこの例だとロードしたアプリケーションがモジュールの関数なのでキャッシュする意味があんまりないことに気づいた。
クラスをロードしてインスタンスをキャッシュするとかだと、意味はある。