#趣旨
ちょっとしたwebアプリをpythonでささっと作る際の検討を、DjangoかFlaskか、という入門者的な二択で調べたのだが、hackernoonでのPaul Bailey氏の「Django too big, Flask too small, Tornado just right!」というエントリが気になったので、抄訳気味に、紹介させてもらう。気になった人は、さらっと読める元エントリをどうぞ。
このエントリを読んだ後に、DjangoやFlaskに対するTornadoの位置づけを考察してみたので、後半に追記しておく(随時更新)。
Tornadoは、情報量の点では難ありだけど、現時点でもpython最速クラスのwebフレームワークとは言えそうだ。
#(抄訳)Djangoでかすぎ、Flask小さすぎ、Tornado、これがぴったり!
##(前振り)
Pythonのweb界隈では、webアプリケーションを作ることになった際には、もっともポピュラーなエコシステムであるDjangoかFlaskのいずれかを選ぶことが通例だ。でも、より良い、そして実用的なwebフレームワークは他にもある。Tornadoが、その一つだ。いくつものユニークな機能を持つTornadoは、あれこれ特化したプロジェクトに取り組む際の、私の秘密兵器になってくれている。
(原文)Quite often in the Python web world when asked to build a web application you choose between the two most popular Python web ecosystems, Django or Flask. However, there are other really capable web frameworks out there that may be better. Tornado is one of those frameworks. Tornado is often my secret weapon for specialized projects because of some its unique abilities.
生まれながらの非同期(Asynchronous From Before You Were Born)
非同期IOは、Node.jsをはじめとする非同期ツールが勃興してから、時代の寵児だ。 Tornadoは非同期操作をサポートするために生み出された。非同期操作を公式にサポートしていないPythonで、Tornadoはきれいな非同期コードを書くための役に立ってきた。...詳しくは Tornado公式の 非同期ネットワーク、及び、コルーチンと並列性 の文書を参考のこと。
(原文)AsyncIO is all the rage these days because of the rise of Node.js and other asynchronous tools. Tornado was built from day one to support async operations. So before Python supported async officially, Tornado was helping you write cleaner async code.
ネイティブのWebsocketサポート
(訳・省略) 要するにDjango Channelsなんかより全然楽だよ、ということ。
##サードパーティのログインサポート
Tornadoは、ソーシャルログインをきちんサポートしてくれている。小さなwebアプリを作る際には、とても役に立つ。
(原文)Tornado comes with support for social logins right out of the box. I find this really convenient because for smaller web apps, if I need a login system, I like to use third-party logins.
##CLIオプションとConfigファイルのパース
Tornadoは、オプション/Configファイルユーティリティをビルトインで持つ。これは、設定オプションを持つようなwebサービスを書くときなどに快適だ。
(原文)Tornado also includes built in options/config file utilities. Again this just really makes it convenient to write web services and package in configuration options that make it operate like other common command line tools.
##その他の特徴
Tornadoには、webフレームワークに期待されるであろう、以下のような機能も持つ:
テンプレート、ルーティング、クッキー操作、リクエストのパース
#Tornadoの使い所についての考察
##1) ふつうのwebフレームワークとして使うには難あり。
2009年に生み出されたTornadoの概要については、wikipediaが簡潔にまとめてくれている。
https://ja.wikipedia.org/wiki/Tornado_(Webサーバ)
実績十分、と言いたいところだけど、情報(特に日本語情報)は少ない。Tornadoについての日本語エントリーの多くはハローワールドレベル。じゃぁ,githubなどに公開されているtornado関係のプロジェクトはどうかというと、djangoやflaskと比べ、メジャーなものは少ない。強いていうと、非同期を得意とするフレームワークであるためか、mongodbやredisなどNoSQLを裏方にするプロジェクトが多いことが目に付く。逆に言うと、RDBを用いる普通の業務アプリケーションのようなものは、相当に気合の入った人でないと厳しそう。
また、上のwikiを見るに、NoSQLと組み合わせて使う場合に、nginxなどの裏方で非同期性・並列で動かすとけっこう速いということに期待したくなる。けれど、最近のwebベンチを見るに、TornadoがCやjavaやGoで書かれたスピードキング的なwebに対抗できるようなものではないことが示されている。
https://www.techempower.com/benchmarks/
###ただし、TorCMS
ひとつだけ、スター数は少なめだが、PostgreSQL(+redis)をバックエンドとした実用的なCMS(TorCMS)が中国のGEO系エンジニアの方によって開発されている(MITライセンス)。
https://github.com/bukun/TorCMS
昨年から作成が開始され、ユネスコのサイト等での稼働実績あり。コードはクリーンに思える。
http://drr.ikcest.org
###(7/24追記) flaskに比し、パフォーマンス面でメリット有り
WAFのパフォーマンスとしては、django < flask < tornadoである模様。
以下でソースコード付きでベンチマーク(ApacheBench)してくれている。
https://gist.github.com/andreif/6088558
ベンチマーク結果(平均値)を引用:
pure_flask: 90
pure_tornado: 242
gevent_flask: 135
tornado_flask: 93
gevent_tornado: 297
ベンチマークしたandreif氏の結論:
終わりに、僕はTornadoの直截的な書き方が好きなんだ..(to write large projectのところの掛かりは不明)..Flaskのスタイルはないね。僕はTornadoにこだわるよ。
In the end, I like the straightforward style of Tornado and not the Flask way to write large project (using blueprints), So I sticks with Tornado.
TornadoとFalskの書き方(style)については、上のgist内のコードを参考のこと。
私は、ベンチマーク結果のもっとも良かった『Tornado with Gevent』の例が気になった。
import tornado.wsgi
import gevent.wsgi
import pure_tornado
application = tornado.wsgi.WSGIApplication([
(r"/", pure_tornado.MainHandler),
],**pure_tornado.settings)
if __name__ == "__main__":
server = gevent.wsgi.WSGIServer(('', 8888), application)
server.serve_forever()
##2) "Websocket ready"なhttpサーバとしては使い所あり。
しかし、tornadoに関する数年前のエントリを見るに、WSGIサーバとしてGunicornやgeventなどと比較した記事が多い。GunicornやgeventなどのWSGIサーバは、djangoやflaskなどの「下回り」のhttpサーバとして用いるもの。当然、tornadoも下回りで用いることができる。
このことの直近での意味合いについて、気づかせてくれたのが、「PythonでシングルバイナリでProductionReadyなhttpサーバ」についてのエントリだ。
こちら、flaskとtornado.wsgiとを組み合わせてPyInstallerでバイナリ化し、Dockerコンテナ等に固めるという試み。これが、ほんとにProductionReadyならばベンダーロックインを避けつつのデプロイ手段になってくれそう(docker対応進めているherokuでもgunicornに代わるベンダーニュートラルな選択肢となるのかな)。
##3) flask + tornadoのいいとこ取り(は、ありうるのか?)
冒頭の「Djangoでかすぎ、Flask小さすぎ、Tornadoぴったり!」の話にやや戻るのだが、本体が小さすぎるFlaskと、別途の得意な点のあるTornadoとを組み合わせの可能性と実用性が少し気になった。すなわち、Flaskをメインで使いつつ、Tornadoのwebsocket機能なども使う、といった選択肢。
もちろん、これは話を複雑にする。githubでは、FlaskとTornadoとを効果的に組み合わせたプロジェクトは見つけられなかった。
ひとつだけ、「一通り機能の揃ったFlask on Tornado with Nginx」という実用性を感じるテンプレートがあったが、中を見るとTornadoは純然たるWSGIサーバとして使われているのみ。
https://github.com/AndreiD/Flask-Easy-Template
このテンプレートからフォークした、Flaskベースのweb APIデモの方では、gunicornがWSGIサーバとして使われている。
https://github.com/systemaker/python-flask-web-api-demo
ということで、flask + tornadoのいいとこ取りはあんまなさそうなのだが、flask on tornadoは(もちろん、django on tornadoも)十分ありなので、今後、djangoやflaskする際には、tornadoのことも頭に置いておこうと思う。
tornadoを深掘りすると、別の世界が見えてくるのかも。
(追記)Django on tornadoでwebsocket
Django on tornadoでwebsocketの取り組みを発見した(7/22初コミット)。
https://github.com/teo3707/web_socket
ただいま、Django学習中なので、ちょっとうれしい。
ちなみにwebsocketクライアント側はflutter(dart)でiOS/androidに対応に取り組み中。