PythonでWebアプリを作成しようとすると、まず頭によぎる問題点として
- 遅い
- 複雑(Webサーバー、APサーバー、WSGI、ASGI、etc...)
- 学習コストが高い
の3つが上がる人が多いと思います。一見Pythonと聞くと、シンプルでとっつきやすい印象がありますが、Webの場合は話が別。無駄に覚えることが多いし、遅いしでPythonの良さが台無し...
じゃあ、**操作が簡単で速くて学習コストが低い(すぐに使える)Webフレームワークを作ろう!!!**ということで今回flyというオープンソースのWebフレームワークを作りました。
どれくらい速いの?
Webフレームワークとして一番大事なポイント。どれくらいの処理速度のWebフレームワークができたのかというと、
-
Nginxとほぼ同等の静的ファイルのレスポンス速度
-
他のPythonのWebフレームワークよりも100倍以上速い(条件にもよる)
Webサーバーとしてのパフォーマンス結果
アプリケーションサーバーとしてのパフォーマンス結果
静的コンテンツ・動的コンテンツに対応していてどちらでも速い結果をもたらすことができました。もちろん併用することもできて、扱いやすくなっています。
もしも需要があれば、具体的なPythonで速いプログラムを作成する方法などの記事も書こうかなと思っています。
なんでPythonなのに速いの?
Pythonは遅い言語としても有名?ですが、なぜ速くできたのかをご紹介していきます。
Pythonはインタプリタ言語だというのはご存知の人も多いと思いますが、実は工夫をすることでコンパイル型言語のようにPythonのコードを扱うことができます。
通常インタプリタ言語は、
- ファイル(.py)を読み込む
- 構文に間違いがないかチェックする
- メモリにインスタンス(実体)を割り当てる
- 2.で解析した結果の通り、実行する
といったステップになっています。最適化はされてはいるものの、C言語やC++のようなコンパイル型言語に比べるとかなり遅いです...(そもそもCやJavaがコンパイルされてつくられたpythonコマンドの上で動作するPythonはどうあがいてもCにスピードが勝てない...)
ただ、PythonにはCPython APIという拡張モジュールを作成するための手段が存在します。このAPIを使用することでPython組み込みのクラスや関数などを作成することができるのです。上のステップでいうところの、1,2の部分を省くことができます。
Python インタプリタに対する様々なレベルでのアクセス手段を C や C++ のプログラマに提供しています。この API は通常 C++ からも全く同じように利用できるのですが、簡潔な呼び名にするために Python/C API と名づけられています。根本的に異なる二つの目的から、 Python/C API が用いられます。第一は、特定用途の 拡張モジュール (extension module) 、すなわち Python インタプリタを拡張する C で書かれたモジュールを記述する、という目的です。第二は、より大規模なアプリケーション内で Python を構成要素 (component) として利用するという目的です; このテクニックは、一般的にはアプリケーションへの Python の埋め込み (embedding) と呼びます。
この拡張モジュールを使用することで、PythonからC言語の関数を呼び出すことができます。これをゴリゴリに活用してあげることで、C言語と同じ速さのサーバーを作ることができます。
ざっくりいうと、numpyやpytorch, tensorflowなどと同じように低水準(機械語に近い)言語をPythonの中から使うことで速度向上させています。
ちなみに、Cython(CPythonとは別)というプログラミング言語もPythonとCのいいとこ取りのような感じで、Pythonの拡張モジュールを作ることができます!
#速さと使いやすさの両立
flyを見てもらうとわかる通り、ほぼほぼC言語になっています。
Cythonは書きやすいといったはもののC言語の知識やオリジナルの言語設計になるので(.pyxやら.pxdなどPythonでは使わない拡張子が登場する)、学習コストは高め。Pythonに比べて構文の資料などは圧倒的に少なめ。
ここはある意味の妥協点で、flyの場合は速さを求めた結果、Pythonで使用する時の使いやすさだけを求めてあとはWebフレームワークとしての速さに全振りしました。なので、使っていただける人が多くなったらPython用のチュートリアルページも作らないといけませんね!
Pythonにもasyncioという非同期IO専用の組み込みライブラリが存在します。純粋なCにはスピード面で及ばないものの、かなり速くなっているので極端に速さを追求しない場合は、基本的にasyncioで大丈夫だと思います。
#flyの特徴
- HTTP2の標準装備(
実装するのクソめんどかったwww) - SSLの標準装備
- とにかくシンプルな構成(特に設定ファイル)
この3つかflyを象徴する特徴になっています。(nginxなどをプロキシで使ってもいいですし、HTTPSで直接使うのもありだと思います。)
ユーザーが使う部分については、できるだけ直感的に操作できることを目標するためFlaskを参考に必要最低限をコンパクトで表現できるように実装しました。あとは
ex.
from fly import Fly
app = Fly()
# マウントすれば、そのディレクトリ以下が静的コンテンツとして登録される。
app.mount("./mount")
# デコレータを使い、リクエストのHTTPメソッドとパスを指定する。
# もしも一致するリクエストがあれば、この関数が実行される。
@app.get("/")
def index(request):
return "Hello, World!"
HTTP2やSSLに関しては、設定ファイルに記述するだけ(HTTP2かHTTP1.1かはクライアントとのやり取りで自動的に決定されます。)で、Pythonの実装部分に関しては気を使う必要はありません。
設定ファイルに関しても、
workers = 1
ssl = on
ssl_certificate_file = conf/server.crt
ssl_certificate_file_key = conf/server.key
nginxやApacheのように独自の言語?は使用せず、イコールでつないだ等式で設定できるようにしました。こういった細かいところは、使っていきながら変更していきたいと思います。。。
まとめ
まだまだ実用化という観点では実績がありませんが、シンプルで速いWebフレームワークを目指して今後も引き続き更新を続けていきたいと思います。
興味や質問等ありましたら、コメントをお待ちしています。
参照
PythonのWeb frameworkのパフォーマンス比較 (Django, Flask, responder, FastAPI, japronto)