PythonのWebアプリケーションフレームワーク Sanicを試す

PythonのWebアプリケーションフレームワークについて、Flaskからの移行先としてSanicが有力そうなので調べています。

Sanic is a Flask-like Python 3.5+ web server that’s written to go fast.

Sanicは著名なイベントループライブラリであるuvloopを利用しており、Node.jsのように非同期I/Oによる高効率なHTTPリクエスト処理が可能です。また、SanicはFlaskとよく似たシンタックスを提供しているため、他のフレームワークよりも移行コストを抑えることができそうです。

環境

サーバ環境

  • Ubuntu 16.04 (x86_64)
  • CPU 3.30GHz 4core / 8GB RAM
  • Python 3.6.4
  • Sanic 0.7.0 / Flask 0.12

クライアント環境

  • macOS High Sierra 10.13.4 (x86_64)
  • CPU 1.70GHz 4core / 8GB RAM

ネットワーク環境

注意点としてはSanicは asyncio および async/await 機能を利用しているためPython3.5以降が必要です。Python3移行は2018年現在なら世間的にもけっこう進んでいるとは思いますが、3.5以降となると業務で使っている人はまだ少ないかもしれません。

使い方

公式でFlask-likeと謳っているとおりインタフェースはFlaskとよく似ているので移行作業自体は楽だと思います。

Flaskでhello, world

Sanicだと以下のようになります。まさにFlask-likeですね。

上記SanicのコードではPython3.5以降で正式に利用できるasyncキーワードが現れています。C#やNode.JSなど他言語におけるasync/awaitと同等の機能が言語標準で提供されるようになりました。Sanicを使う場合は、個人的にNode.js開発でよくやってしまうawaitの置き忘れも気にしなくて良いのは助かります。

JSON対応

JSONレスポンスを返すには以下のように書きます。FlaskもSanicどちらも使いやすいと思います。パッケージ構成はSanicの方が整理されてる気はしますがどうでもいいレベル。

ちなみにJSONパーサーは独自実装ではなく外部モジュールが使われています。Flaskを使う場合はsimplejsonをセットで入れておくと良いです。Sanicはujsonが必須となっています。

  • Flask: simplejson or json
  • Sanic: ujson

JSONモジュールのベンチマークに関しては以下のサイトが参考になります。

ujson優勢みたいなのでJSONレスポンスを含めたベンチマークだとFlaskが不利だと思うので、テキスト(“hello,world”)のみを返すシンプルなコードで計測します。ただ、有利不利とか言ってしまうとuvloopに乗っかっているSanicはズルいという話になってしまいますけども。。

ベンチマーク

前述のサンプルコード(‘hello, world’文字列を返却)で組み込みHTTPサーバのベンチマーク。同一LAN内でクライアント/サーバの2台用意して計測、ツールはwrk2を利用しました。

ネットワーク周りのカーネルパラメータは以下の通りです。TCPのsyn backlogをsomaxconnの値と合わせて増やす程度のチューニングはしてあります。TCP接続を使い回す設定にもしていますが、今回のベンチマークでは意味ないですね。

まずは4スレッド同時100コネクション、2000rpsで30秒間負荷を与えました。

レイテンシ周りの計測値を見てみるとわかりますが、これだけでもかなり性能差があることが読み取れますね。低スペックのサーバで動かしているのですがSanicの方はまだまだ余裕がありそうです。次は4スレッド同時200コネクション、4000rpsで30秒間負荷を与えました。

Flaskの方は限界が見えてますね。タイムアウトも発生しています。ネットワーク周りのカーネルパラメータが未調整だと他のソケット関連エラー等も併せて出てくるはずです。一方でSanicの方は安定した性能です。uvloopすごい。

(横軸の数値が見えてないですが、1/ (1 – Percentile) 刻みの対数表示となっています)

おわりに

Pythonのasyncioに対応したフレームワークはSanic以外にもいくつかありますが、既存のFlask製アプリケーションの移行コストを最小化するために、Flask-likeなSanicを採用するのは悪くない選択だと思います。2,3年前にFlaskの代替として、Falconというフレームワークに移行する作業もやったことがあるのですが、使い方がFlaskと結構違うので移行コストが高かったです。その点、Sanicを使えばその辺りの面倒を省くことができますね。Flaskプラグインの移植性も高く、ベストプラクティスの流用も行いやすいのではないでしょうか。

また、Sanicはasyncioのdrop-in replacement実装としてuvloopを組み込んでおり、uvloop自体はlibuvをベースにしているので実績も十分、性能も保証されています。Python3.5以降が必要なので実業務で採用するのはまだ少しネックになりそうですが、そこは時間が解決してくれる部分でしょう(スタートアップのサービスとかなら勢いで採用しても良いかも?)。他にuvloopをベースにしたフレームワークとしてはjaprontoというのもあります。Flask-likeでなくても良いならこちらも選択肢としてアリですね。

まずはプライベートワークで作っているPython製WebAPIはSanicに移行してみようと思いました。前回紹介した pyvips(libvips) を利用すれば高速な画像処理サーバもPythonで簡単に書けそうです。

参考

あわせて読む:

2 Thoughts

  1. ベンチマークにて、C10K問題に対応しているかどうか明確にして頂けますと、参考になります。
    今の所対応しているPythonのフレームワークは、Cycloneフレームワークのみです。

  2. Sanicと同じく、Pythonのフレームワークに、Japrontoフレームワークがありまして、こちらは、1秒間に100万件処理出来るそうです。Sanicも早いらしいですが、ベンチマーク結果が良くわからないブログ記事が多いようです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です