表題の通り、WEBrickはシングルスレッドなアプリサーバではなくマルチスレッドです。

にも関わらず、Web上にはWEBrickがシングルスレッドであるかのような記事が多く、Wikipediaでも

と記載があり、とても誤解をまねきやすいです(デフォルトで、と言っているので完全に間違っているとは言い難いけど)

今回は、WEBrickがマルチスレッドサーバである根拠と、なぜシングルスレッドと誤解されているのかをRailsのバージョンの話も交えて書いていきます。

WEBrickがマルチスレッドである根拠

どのバージョンを見ても、acceptしたsocketに対してThread.newで別スレッドを作ってそのスレッド内でHTTPハンドリングをしてます。かなり省略していますが、こんな感じなコードになっています。

試しにsleepするだけのRackアプリをを用意して、curlを連続で叩くと、sleepで指定した秒数の後に同時にレスポンスが返ってきます。

なぜシングルスレッドであると誤解されているのか

Rails4ではrails serverしたときのデフォルトのアプリサーバとしてWEBrickが使われます。Rails4の時点ではautoloadがスレッドセーフではないため、同時アクセスがある場合にconst_missing内の定数ロードによってコンフリクトが発生する可能性があります。

そのため、Railsではこのようなスレッドセーフではないautoloadに起因するエラーを防ぐため、WEBrickにおいてはRack::Lockのミドルウェアによって同時実行制御を行っており、結果としてシングルスレッドであるような振る舞いをrails serverに付与しています。

コメントにも記載があるようにproductionでWEBrickを使っているユーザに対して、後方互換のためにRack::Lockを入れた、とのことです。後方互換と言っているのはおそらくRails3のallow_concurrencyのデフォルトがfalseなので、そのままproductionで利用するとシングルスレッドで動くわけですが、Rails4のallow_concurrencyのデフォルトはtrueなので、そのままproductionで動かすとマルチスレッドで動いてしまいます。Rails3でもallow_concurrencyをtrueにすればWEBrickでもマルチスレッドで動いたりautoloadのバッティングが発生することは確認済みです。

まとめると、Rails3のデフォルトではallow_concurrencyはデフォルトfalseであったことや、Rails4は後方互換のためにWEBrickをシングルスレッドで動かしていて、そのためWEBrick自体はマルチスレッドなアプリサーバであるにも関わらず、シングルスレッドであるかのような記事が見受けられる、ということになります。

ちなみにコードや挙動の確認以外にも以下のような記事があり、マルチスレッドであることを確認しています。

Rails5ではどうなっているか

Rails5では特に設定をしなくてもマルチスレッドで動きます。コードを読むとmiddlewareがごっそりなくなっています。

また、Rackのミドルウェアもallow_concurrencyが明示的にfalseが指定されていなければコンカレントに動きます。Rails4まではデフォ値のnilのときはRack::Lockを入れるような処理になっていました。

さらにautoload自体がDependencies.load_interlockによって、スレッドセーフになっているっぽいです。そのためCircular dependency…のエラーが起きづらくなっているようです(というか起こす術がわからん…)

まとめ

  • WEBrickはacceptの度にThread.newを作って別スレッドにHTTP処理を委譲するマルチスレッドサーバである
  • Rails4ではWEBrickがデフォルトのアプリサーバとなっており、rails serverしたときにシングルスレッドで動作するような設定になっている
    • Rails4ではautoloadがスレッドセーフではなかったためシングルスレッドで動作するような仕組みにしていたと思われる。
    • Rails3のデフォルト値の関係で、後方互換のためにシングルスレッドで動作するようにしたっぽい
  • Rails5ではWEBrickでrails serverしたときも、マルチスレッドで動作するようになっている。
    • Rails5ではautoloadがスレッドセーフになったとともに、productionではauto_loadではなくeager_loadを推奨する作りになっているのでマルチスレッドでも問題ないような実装になった

参考URL