Google Open Source Peer Bonus Program で受賞されてたという話

mopemope
7 min readAug 22, 2018

--

どうも、僕です。

Google Open Source Peer Bonus Program とかいうのに選ばれたのでその辺りの話を書きます。

この場を借りて Google に心よりお礼申し上げます。
ジュリアナー!!トーキョー!
あざーす!

Meinheld の開発を振り返る

受賞は Meinheld という WSGI サーバーの開発者として表彰されました。
国内ではあまり知られていないかも知れませんがそこそこ使われています。
Mozilla の MDN Web Docs が有名でしょうか。
(その他のMozillaのサイトでも使われているようです)
過去には買収前の TestFlight, httpbinなども使っていました。
(TestFlight の ChangeLog に使用してた記録があった。要は他に乗り換えたってログなんだけど)

非公開時代

GitHub 上は 2010–07–30 が初コミットとなってますが、元々は picows という小さな Web サーバーがベースになっているので恐らく 2009 年ぐらいには書き始めてるはずです。
元々は C 言語の練習、非同期な HTTP サーバーをそこそこ真面目に実装するという目的で開発してたのですが、仕事でも使用できそうな雰囲気がでてきたので公開するに至りました。
パフォーマンスに関する部分は picows 時代に検証を重ねたため十分な速度が得られていました。
当時、高速な WSGI サーバーといえば mod_wsgi ぐらいで Apache と合わせて使用するケースが多かったです。しかし、やはり Apache 上で動作させるための独自な設定や考慮が必要ということでとっつきにくさがありました。
一応他にも C で書かれているスタンドアロンな WSGI サーバーはありましたがすぐ SEGV で落ちるようなシロモノや PEP を満たしてなかったりと散々な印象がありました。
書き始めの頃はそこまで複雑ではなく HTTP のプロトコルのパーサーも Ragel ベースで書いたものを使っていました。
これは当時 Ruby の高速な Rack サーバー mongrel などがこの手法で高速なサーバーを開発していたのを真似た記憶があります。
mongrel のパーサーはかなり雑だったのでそのままでは厳しいということで ebb が使っていたものを流用し、手を入れて使っていました。
余談ですがその後、ebb の作者はそれも捨て、手で書かれた http parser を元に node.js を開発することになります。

WSGI サーバーは PEP で仕様が詳細に決まっています。
基本的には実装するだけなので簡単そうに思えるかも知れません。
ですがやってみるとこれがなかなか大変で、苦労した覚えがあります。
(余談ですが、私は PEPを 248, 333, 3333 と実装したことがあります。どれも大変です。)

初版リリース

初版リリース時には幾つかの機能を改善、追加しました。

  • http parser を変更
  • セキュリティの向上 (slowloris, メモリの確保による攻撃)
  • gunicorn worker のサポート

リリース後、そこまで宣伝はしなかったのですが、ある機能により Meinheld はユーザー数を伸ばします。
それは Gunicorn Worker のサポートです。
Python でマルチコアを使い切るためには複数プロセスで処理を行う必要があります。
元々はプロセスを管理機能も実装する予定でした、Cで。
と言ってもネットワークサーバーの複数プロセス管理など書いたことがありません。
実装の参考になるものを探していたところ Gunicorn を見つけました。
C 言語で再実装するためソースを読んでいたところ、どうやら外部ライブラリで Worker を設定できそうであることがわかりました。
Gunicorn 開発者が意図してそのような実装にしていたかは不明ですが、プロセス管理は Gunicorn に任せることしました。この判断はとても先見性がありました。
現在 Gunicorn は WSGI サーバーのプロセス管理ライブラリで最も使用されています。

協調スレッド、I/O 戦略、Go 言語との共通点

Meinheld には私が仕事で使うに耐えうるエキスパートな機能が実装されています。
その機能とは WSGI Handler が全て協調スレッド(軽量プロセス)上で実行されるという機能です。
これは初版時から実装する予定ではありましたが、一筋縄ではいかなかった記憶があります。
Meinheld 上で実行する WSGI アプリケーションの I/O は基本非同期I/Oで処理し、I/O 待ち時間には他の軽量プロセスを実行することで CPU をできるだけ使い切ろうとします。
いくら高速な WSGI サーバーでもアプリケーション側が遅くてはそのメリットはありません。
アプリケーション側で処理する時間が多くなるケースではせっかくの WSGI サーバーの高速化が誤差レベルになってしまいます。
そのため、全体のスループットをあげるべく協調スレッドを使用し、 I/O 待ちにはスレッドを切り替えるという戦略が取れるような設計をしました。
この設計は eventlet で採用されていた設計で当時 IO trampoline という名前で呼ばれていました。
Meinheldではそれを C 言語で実装しておりとても高速に動作します。
I/O を全て非同期にし、軽量プロセスを使用し、I/O 待ちで他の軽量プロセスを実行するモデルは Go のランタイムでも使われています。
Meinheld に greenlet (軽量プロセスライブラリ)を組み込み始めたのが 2010 、go1が 2012 リリースなので1年は先取りしてる実装をしていたということになります。

さらなるユーザーの増加、そして

さらに Meinheld の名前が知られるようになったのは Mozilla に所属していた Tarek Ziadé 氏にベンチマークの紹介がきっかけです。

これを機にベンチマークで Meinheld がよく使われるようになりました。
Mozilla でも使われるようになったのはこの頃からだと記憶しています。
(確か社内で使っているというようなことを聞いた記憶があります)
TechEmpower Framework Benchmarks でも使用され、高スコアを残しています。
(WSGI の仕様を満たしているサーバーでは今でもトップクラス)
その他 Meinheld は C 言語で書かれているため Python3 対応はマクロで切り替えて実装されています。
Python 2系, 3系 で処理自体別々にクリーンに実装しているため安定した動作をします。
Python C API はかなり互換性に気を使われているため、変更されることは極稀です。
そのため、新しいPythonがリリースされてもほぼメンテをせずに安定した動作をします。

最後に

本当はまだまだ書きたいことがあるのですが、長くなってしまったので今回はここまで。
最後に Meinheld を使ってくれたユーザー、バグを報告してくれた方、メンテナンスを手伝ってくれた @methane 氏 、Meinheld の開発にあたり、 ご助力いただいたことに心より感謝申し上げます。

--

--