.htaccess
<ifmodule mod_rewrite.c> RewriteEngine On RewriteBase /spa/ RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /spa/index.html [L] </ifmodule>
/spa/index.html がSPAのファイルです。
.htaccess
<ifmodule mod_rewrite.c> RewriteEngine On RewriteBase /spa/ RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /spa/index.html [L] </ifmodule>
/spa/index.html がSPAのファイルです。
ChromeでCanvasのアニメーションが、とても遅くてカクカクとしか動かない。
どうにも動作が遅いので、調査したところ以下の設定をEnabledにしてやると問題ないレベルで滑らかに動作するようになった。
ChromeのURL欄に chrome://flags/ と入力すると以下のような画面が現れる。
以下の4つをEnabledにした。
とりあえず解決されてよかったですが、この現象が発生しているのは古めのPCで、何が原因で遅くなってしまったかが不明です。
Admin画面のFileFieldからアップロードした直後のファイルの処理がなかなか分からなかったが以下のようにmodels.pyにdef save(self, *args, **kwargs):を追加して解決しました。
例 models.py:
参考
私たちのWeb アプリケーションの中には、 mod_wsgi を使っているものがあります。 これまで、アプリをデプロイする際は、 VirtualHost あたり一つだけ WSGIScriptAlias を指定するような使い方をしてきたのですが、最近、新たなアプリを組み込むために、一つの VirtualHost に複数の WSGIScriptAliasをもたせる方式を検討することになりました。
結局この方式は見送られ、別のやりかたに変えたのですが、その経緯をお話したいと思います。
WSGIScriptAlias は、特定のパスへのリクエストを mod_python が処理するように指示するためのディレクティブです。
単純な Web アプリをデプロイするときは、WSGI アプリを application という名前で定義した Python コードの入ったファイルを指定します:
<VirtualHost *:80> WSGIScriptAlias /hoge /path/to/app/hoge.wsgi </VirtualHost>
これで、 mod_wsgi は /hoge 以下のリクエストを hoge.wsgi で処理しようと試みるようになります。 複数のアプリケーションをデプロイしたければ、
<VirtualHost *:80> WSGIScriptAlias /hoge /path/to/app/hoge.wsgi WSGIScriptAlias /moge /path/to/app/moge.wsgi </VirtualHost>
のように、エンドポイントごとに別のスクリプトを指定していけばよいはずです。
実際、開発を行ったチームも、この構成で一通り動作確認をして、単体テストレベルでは /hoge と /moge の両方のアプリケーションがそれぞれ正常に稼働することを確認していました。
しかし、リリースバージョンをテストする環境で自動テストを流したところ、突然テストが失敗しはじめました。既存のアプリケーションで 404 エラーが多発し始めたのです。
開発チームは、最初はリリースバージョンの他の変更が原因ではないかと考えたので、他の変更をロールバックしました。いったん症状は改善したかにみえます。・・・しかし何度かテストを走らせると、やっぱりエラーが発生します。しかも、新規のアプリケーションにも同じような症状が現れ始めました。
この時点で、 mod_wsgi の挙動を疑い始めます。テスト用サーバの config を確認したところ、 WSGIApplicationGroup が SERVER に設定されており、これが原因でした。
WSGIApplicationGroup とは、ひとことでいえば、「リクエストを mod_wsgi のどのインタプリタで処理するか」の設定です。
Apache では、リクエストを処理する際、ハンドラと呼ばれる処理を順に呼び出します。 mod_wsgi のようなモジュールでは、このハンドラを登録しておき、 config に設定したパスに一致するリクエストが来たときに、WSGIスクリプトを実行してリクエストを処理するような仕組みになっています。
スクリプトの実行は mod_wsgi に組み込まれた Python インタプリタ上で行われ、毎回インタプリタを初期化したり、モジュールを読み込み直さなくてもよいように、インタプリタを再利用する仕組みがあります。
しかし、Apacheが受け取るすべてのリクエストを「一つのインタプリタ」の再利用で処理しているとマズいことになります。
例えば、内容の違う同じ名前のモジュールが使われていると、先にロードした方がキャッシュされ、後続のリクエスト処理で参照されてしまいます。
この挙動を実演するために、以下のようなライブラリを考えましょう。(下記のコードは少々残念な作りですが、そこはお察しください):
# lib.py import config def site_name(): return config.SITE_NAME
このライブラリを使って、以下のようなアプリを組みます:
# app.wsgi import os, sys; sys.path.insert(0, os.path.dirname(__file__)) from lib import site_name def application(environ, start_response): start_response('200 OK', []) return ['{}, {}'.format(__file__, site_name())]
複数の VirtualHost A と B で運用してみます。 このとき、A と B の config.py にそれぞれ SITE_NAME='SITE A', SITE_NAME='SITE B' を設定しておきます。:
<VirtualHost *:8081> ... WSGIScriptAlias /app "/home/developer/app/app1/app.wsgi" </VirtualHost> <VirtualHost *:8082> ... WSGIScriptAlias /app "/home/developer/app/app2/app.wsgi" </VirtualHost>
この状態で、それぞれの VirtualHost に繰り返しアクセスしてみます::
watch -n .5 curl http://localhost:8081/app
各々、'...app1/app.wsgi SITE A', '...app2/app.wsgi SITE B' が表示されます。期待した通りの動作ですよね。
しかし、 WSGIScriptAlias を %{GLOBAL} に設定すると・・・両方のサイトで表示が混じり始めます。 WSGI スクリプトは app1 なのに SITE B と出たり、その逆になったり。リクエストを繰り返していると切り替わったりします。
実は、デフォルトの mod_wsgi の動作ではこういう挙動は滅多に起きません。 mod_wsgi は、一つのインタプリタですべてのリクエストを処理するのではなく、内部的に複数のインタプリタ(サブインタプリタ)を生成して、リクエストの内容に応じて使うサブインタプリタを区別しているからです。
この区別の方法を指定するための設定値が WSGIApplicationGroup で、デフォルトの設定値は "%{RESOURCE}" になっています。
設定できる値とその意味は http://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIApplicationGroup.html で詳しく解説していますが、おおざっぱにいうと以下の通りです。:
RESOUCEが指定されているデフォルトの状態では、各々のサブインタプリタは "<host名>:<ポート番号>|<WSGIScriptAliasのパス名>" という名前で区別され、VirtualHostごと、かつアプリごとにインタプリタを使い分けているので、アプリ間でインタプリタを再利用することがありません。
ところがGLOBALを指定すると、すべてのリクエストを(他のVirtualHostへのアクセスも含めて!)一つのインタプリタで処理しようとします。
我々の環境では、対象ホストに {SERVER} の設定が入っていたためこの事象を踏んでしまったのでした。 SERVERの設定では、VirtualHost間は区別するので、VirtualHostごとに単一のアプリをデプロイしているうちは問題になりませんが、一つのVirtualHostで複数アプリを動かすようになると・・・ひどい目にあいます。
mod_wsgi のドキュメントによると、 サブインタプリタ間は「ほぼ完全に区別」されていて、各々のサブインタプリタに影響を与えることはまずないとされています。とはいえ、拡張モジュールの中にはサブインタプリタ間で問題を起こすものがある (http://modwsgi.readthedocs.io/en/develop/user-guides/application-issues.html#multiple-python-sub-interpreters) ことや、対象のシステムはできるだけ安全に(保守的に?)動かしたかったところから、今回は導入を見送ることになりました。
mod_wsgi は、共有ライブラリの形式をとっています。そのため、Python から DLLへのアクセスを可能にする ctypes モジュールを使って、動いている Apache プロセス中の mod_wsgi の中をのぞきこむことができます。
例えば、以下の内容のwsgiアプリケーションをmod_wsgi上で動かすと、 mod_wsgi の静的領域にある wsgi_interpreters というオブジェクトにアクセスできます。このオブジェクトは、サブインタプリタの名前とインタプリタオブジェクトを対応付けていて、実際に mod_wsgi がリクエストを処理するインタプリタを検索するときに使われます。 Pythonの辞書オブジェクトなので、簡単に内容を表示できます:
from json import dumps from ctypes import cdll, py_object dll = cdll.LoadLibrary('/usr/lib/apache2/modules/mod_wsgi.so') def application(environ, start_response): start_response('200 OK', [('Content-type', 'application/json')]) interpreters = py_object.in_dll(dll, 'wsgi_interpreters').value response = { 'i': [(k, str(v)) for k, v in interpreters.items()], } return [dumps(response).encode('utf-8')]
mod_wsgi のドキュメントによれば、WSGIApplicationGroup が %{GLOBAL} のときには、全てのリクエストは最初に生成されるただ一つのインタプリタで処理され、そのインタプリタの名前は空文字列になります。実際、実行してみると、レスポンスは以下のようになります。いくつアプリケーションを追加しても、この辞書は大きくなりませんし、Apacheのプロセス自体が入れ替わるまで、インタプリタオブジェクトも変化しません::
{"i": [["", "<mod_wsgi.Interpreter object at 0x7f1728fd3660>"]]}
%{SERVER} に変えて、それぞれのサーバにアクセスすると、別のサブインタプリタが登場します:
{"i": [["", "<mod_wsgi.Interpreter object at 0x7f1eb95ec660>"], ["10.0.X.X:8081", "<mod_wsgi.Interpreter object at 0x7f1eb95ec9f0>"], ["10.0.X.X:8082", "<mod_wsgi.Interpreter object at 0x7f1eb81c16f0>"]]}
%{RESOURCE} に変えると、インタプリタ名にパス名も入ります。
{"i": [["", "<mod_wsgi.Interpreter object at 0x7f45345ca660>"], ["10.0.X.X:8081|/app", "<mod_wsgi.Interpreter object at 0x7f45345ca9f0>"], ["10.0.X.X:8082|/app", "<mod_wsgi.Interpreter object at 0x7f4530166fc0>"], ... ]}
キーが空文字列のインタプリタは、どの設定の時も存在しますが、GLOBAL の設定以外で直接リクエストを処理することはありません。こうして実際に mod_wsgi の中身をのぞくと、ドキュメント通りにインタプリタに名前がついていることがわかって、ちょっと安心できますね。意外なところで使える ctypes 。楽しいです。
mod_wsgi の WSGIApplicationGroup をうっかり設定してしまってハマったというお話でした。 それだけではもったいないので WSGIApplicationGroup を変えたときにサブインタプリタの管理がどのように変わるか調べてみました。 でも、そろそろ mod_wsgi は卒業したいですね。
分類 | 説明 | font-weight | 接頭辞 (HTMLに記述) |
---|---|---|---|
SOLID | 通常アイコン、白抜きのもの | 900 | fas |
REGULAR | アウトラインをとったようなもの | 400 | far |
LIGHT | 線が細いもの | 300 | fal |
BRANDS | Twitterなどのブランドロゴ | 400 | fab |