LL言語でのhot deployとJavaでのhot deploy

JVM Operation Casual Talksで出てた話としてJavaでhot deployってどうしてんの?ってのがありました。

hot deployっていうのはアプリケーションコードを変更してもAPサーバーを再起動せずに反映する技術です。

この辺別に僕は全然知らないし答えを持っているわけではないですが、まあちょっと興味があったのでLL言語でのhot deployとJavaでhot deployを簡単に調べたのでメモっときます。

コードを変更してAPサーバーを再起動する場合、APサーバーが止まっているときにアクセスが行くと困るので、ロードバランサから外してAPサーバーを再起動してまた戻すみたいなことをやるのがオーソドックスな方法のようですが、hot deployだとそういったことをやる必要が無くなります。

Server::Starterから学ぶhot deployの仕組み - $shibayu36->blog;を読むと、PerlのAPサーバーであるServer::Starterでは以下のような感じでhot deployを実現しています。

  • APサーバー起動
  • マスタープロセスがソケットを生成
  • 上記ソケットを共有するワーカープロセスがforkされる。このプロセスがHTTPリクエストを処理する。
  • アプリケーションコードを変更してシグナルを送信する。
  • シグナルを受け取ったサーバー側は上記ソケットを共有する新しいワーカープロセスをforkする。このプロセスが新しいアプリケーションコードを反映したものであり、これがHTTPリクエストを処理する。
  • 古いアプリケーションコードを反映している古いプロセスを止めるためにシグナルを送る。
  • 古いプロセスが終了する。

RubyのAPサーバーであるunicornもだいたい同じような感じみたいですし、http://tech.naver.jp/blog/?p=1369を見る限りnginxも同じような感じみたいですね。

言語は違えど共通しているのはLinuxシステムプログラミングのもとで実現していること。

preforkモデルのもとcopy-on-writeのおかげでメモリ効率もよく、ワーカープロセスがすべて同じソケットを共有することでhot deployの仕組みを OS 任せにしています。

一方Javaのhot deployはというと、JRebelのようにinstrument APIを使って動的にクラスを差し替えるタイプとSeasarのようにリクエスト毎にクラスローダーを作ってクラスを差し替えるという2つのタイプがあります。OSじゃなくてJavaの世界ですね。前者はよく知らないですけど、後者は開発時に使うことを想定した機能でhot deployで運用することは推奨されていないです。まあhot deployで運用したら性能も悪そうですよね。

JavaというかServlet APIを使う限りは1リクエスト、1スレッドの世界です。Servletインスタンス変数を使ってはいけないというコーディングルールがあったりしますが、あれは他のユーザのデータが見えたら困るからで、なんでかっていうとスレッドで動いているから。つまり複数のリクエストでメモリを共有するからです。そしてスレッドだから性能が良いんですけどね。 Servletは1インスタンス、マルチスレッドモデルで複数のスレッドが1つのServletインスタンスを参照するのでインスタンス変数を使ってはいけないです。LL言語のpreforkモデルの世界だと1リクエスト、1プロセスなのでこの手のコーティングルール的なものは無いような気がしますが、どうなんでしょ。コメント欄参照(4/17追記)

Seasarとは別にPlayというJava,Scalaフレームワークにもhot deploy(こちらもクラスローダー差し替えてhot deployしているようです。たぶん。がこちらもSeasarと同じ開発時のみの使用で本番運用でのhot deployは想定していないようです。)の機能があるのですが、このフレームワークServlet APIを使っていません。

その辺は下記ブログに詳しいので読んでみると面白いと思います。

コードのホットスワップを可能にするには、アプリケーションをステートレスに作ることが欠かせない。あなたは疑問に思ったことはないだろうか。なぜ PHP ではホットスワップ出来るのに、JEE だと出来ないのか、と(ここで言っているのは、バックグラウンドで war をデプロイし直すのではなく、本当にアプリケーションコードをホットスワップするという意味だ)。PHP に出来るのは PHP は徹頭徹尾ステートレスだからだ。PHP の場合、2つの連続したリクエストの間で、都度まっさらな新しいプロセスが起動する(確かに今は FastCGI を使っていくらかの最適化を図ることがあるが、根本的なモデルを変更するわけではない)。アプリケーションがステートレスでなかった場合、コードスワップの後にメモリ上の状態を再構築することなんてできない。きちんと動くケースもあるだろうが、重要な変更があった場合はまず動かないだろう。

なぜ Play は Servlet を使っていないのか(Why there is no servlets in Play 翻訳) - ikeike443のブログ