Tornadoとは?Python製高性能非同期ウェブフレームワークの徹底解説(WebSocket・長時間接続対応)

Tornadoとは — 概要

Tornadoは、Pythonで書かれた非同期(ノンブロッキング)ネットワークライブラリ兼ウェブフレームワークです。大量の同時接続を効率よく扱うことを目的として設計されており、WebSocketや長時間接続(long polling / streaming)など、リアルタイム性を要求するアプリケーションでよく使われます。軽量なHTTPサーバ機能に加え、独自のイベントループ(現在はasyncioとの互換)やWebSocketハンドラ、テンプレートエンジン、認証補助機能などを備えています。

歴史と進化

TornadoはFriendFeedの開発チームによって開発され、FriendFeedがFacebookに買収された後にオープンソースとして公開されました。初期は独自のI/Oループとコールバックベースの非同期モデル(tornado.genなど)を中心に発展しましたが、Pythonの標準であるasyncioの普及に合わせ、Tornado 5.xでasyncioとの共存をサポートし、Tornado 6.0で正式にasyncioをデフォルトのイベントループとして採用、かつPython 2のサポートを終了しました。これにより、ネイティブなasync/await構文と互換に進化しています(詳細はリリースノート参照)。

アーキテクチャの概要

  • I/Oループ:イベント駆動の中核。過去は独自実装だったが、現在はasyncioベースで動作(互換レイヤあり)。
  • HTTPサーバ:tornado.httpserver.HTTPServerで非同期HTTPを処理。単体で公開可能だが、実運用ではリバースプロキシ(nginx等)と組み合わせることが多い。
  • RequestHandler群:tornado.web.RequestHandlerを継承してルートごとの処理を実装。
  • WebSocketサポート:tornado.websocket.WebSocketHandlerで双方向通信を扱える。
  • 非同期API:tornado.genやasync/awaitを用いたFutureベースの非同期処理。
  • ユーティリティ:テンプレート、セッション/クッキー補助、オプションパーサ、非同期HTTPクライアントなど。

主な特徴

  • 高い同時接続性能:シングルスレッドのイベントループで多数のコネクションを効率的に処理。
  • WebSocketやストリーミング/長時間接続への対応が容易。
  • 軽量で必要最小限の構成から大規模構成まで柔軟に対応。
  • asyncioとの互換性(最新バージョンでは標準のasyncioを採用)。
  • HTTPサーバ機能を内蔵しているため、外部サーバに依存せずに動かせる。

基本的な使い方(簡単な例)

最もシンプルなHTTPサーバの例を示します(async/awaitスタイル)。

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    async def get(self):
        self.write("Hello, Tornado!")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

同様にWebSocketは以下のように書けます:

from tornado import websocket, web, ioloop

class EchoWebSocket(websocket.WebSocketHandler):
    def open(self):
        print("WebSocket opened")

    def on_message(self, message):
        self.write_message(u"You said: " + message)

    def on_close(self):
        print("WebSocket closed")

app = web.Application([(r"/ws", EchoWebSocket)])
app.listen(8888)
ioloop.IOLoop.current().start()

非同期プログラミングモデル

古いTornadoではtornado.genを使ったコールバック/Futureベースの記述が主流でしたが、現在はPythonのasync/awaitを推奨しています。Tornadoは内部でasyncioのFutureと互換性があり、asyncioのライブラリと自然に組み合わせられます。注意点としては、イベントループはシングルスレッドで動くため、CPU負荷の高い同期処理を直接書くと全体の応答性が落ちることです。その場合はThreadPoolExecutorやプロセス分散でブロッキング処理を別スレッド/別プロセスへ委譲します。

運用とデプロイ

  • 単体でHTTPサーバを提供できるため、軽量サービスはTornadoだけで運用可能。
  • プロダクションでは多くの場合nginxなどをリバースプロキシに置き、静的ファイルはnginxで配信、Tornadoはアプリケーション層を担当する構成が一般的。
  • マルチプロセス化:tornado.httpserver.HTTPServer.bind() / start(num_processes)で複数プロセスを立てる。あるいはsystemdやsupervisor等で複数ワーカーを立てる。
  • SSL/TLSはHTTPServerに設定可能だが、リバースプロキシで終端することが一般的。

メリットとデメリット

メリット:

  • 多数同時接続の処理に強く、リアルタイム通信(チャット、通知、ストリーミング)に適している。
  • 軽量で学習コストが比較的低い(シンプルなAPI設計)。
  • 内蔵HTTPサーバを持ち、単体で運用しやすい。

デメリット/注意点:

  • CPUバウンドの処理は苦手。重い計算は別プロセスに分離する必要がある。
  • 最近のASGIエコシステム(FastAPI/Starlette等)と比べると、エコシステムやミドルウェアが若干少ない場合がある。
  • 古いTornadoのコード(genベース)からの移行で非同期モデルの整理が必要になる場合がある。

他フレームワークとの比較(簡潔に)

  • Flask/Django(従来のWSGI):同期処理中心。多数のミドルウェアやプラグインがあるが、長時間接続や多数同時接続は苦手。
  • ASGI系(Starlette/FastAPI, Uvicorn/Hypercorn等):asyncioネイティブで高性能。Tornadoは同じく非同期だが、Tornadoは内蔵サーバと豊富なユーティリティを持つ点が特徴。
  • Node.js:I/O非同期モデルの代表。エコシステム・言語が異なるが、用途によっては競合することがある。

移行や互換性のポイント

古いTornado(Python 2時代)から最新のTornadoへ移行する場合、以下に注意してください:

  • gen.coroutineやyieldベースからasync/awaitへ書き換えるとコードが読みやすくなる。
  • 独自I/Oループに依存するコードは、asyncio互換のAPIに切り替える必要がある。
  • 外部ライブラリが同期型の場合は非同期版に差し替えるか、Executor経由で呼び出す。

セキュリティと運用上の留意点

  • クッキーの署名やSecure Cookie機能を利用してセッション管理を堅牢にする。
  • CSRF対策や入力バリデーションはアプリ側で適切に行う(Tornadoは軽量なのでフル装備は自分で用意する必要がある場合がある)。
  • 長時間接続を扱う場合、リソースリーク(ハンドラやソケットの未解放)に注意する。

まとめ

Tornadoは「非同期I/Oに強い、軽量で実用的なPython製ウェブフレームワーク/サーバ」として、特にWebSocketや長時間接続を多用するリアルタイム系アプリケーションに適しています。最新バージョンではasyncioを標準採用しており、Pythonの非同期エコシステムとも連携しやすくなっています。一方で、CPU集約処理の切り分けやブロッキング処理の回避など、非同期プログラミング特有の注意点は理解しておく必要があります。用途に応じてTornadoを選ぶか、ASGI系フレームワークを選ぶかを判断すると良いでしょう。

参考文献