uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

内部から見たVert.xとNode.jsとの比較

socket.ioがJavaGrailsから扱えるかを調べている関係でvert.xを調べていて興味深かったので、こちらにあるVert.xの記事を翻訳してみました。JGGUG G*Workshopにおける杉浦さんのVert.x資料もお奨めです。


Vert.xは急速に発達つつあるサーバ・フレームワークです。

世にあまたあるサーバ・フレームワークのいずれもが、多様なプロトコルをサポートし、高速であることが特長であると主張していますが、Vert.xはそれらよりも一歩抜きん出ています。例えば、Vert.xは、サーバサイドのネットワーク環境の確立と操作も対象としています。言いかえれば、Vert.xは、単一サーバ上のデーモン実行だけでなく、クラスタリング環境での複数サーバデーモンの実行を考慮しているのです。

したがって、Vert.xを調査するにあたっては、どのように高性能を実現しているかだけではなく、それが考慮しているネットワーク環境がどのようなものであるかを見ていくことが重要です。Vert.xの構造を調べることには、時間をかけるだけの価値があると思います。

Vert.xの哲学

Vert.xはNode.jsから影響を受けているプロジェクトです。Node.jsと同様に、Vert.xはイベントベースのプログラミング・モデルを提供するサーバ・フレームワークです。したがって、Vert.x APIは、Node.jsのそれと非常に似ています。なぜならこれらのモデルは両方とも非同期APIを提供するからです。

Node.jsはJavaScriptで開発されているのに対し、Vert.xはJavaで開発されています。しかし、単に「Node.jsのJavaバージョン」としてVert.xを理解するのは単純すぎる見方です。Vert.xはNode.jsの影響を受けていますが、Node.jsとは確かに異なるユニークな哲学を持っています。

Vert.xの設計哲学のコアは以下のように要約できます:

  • Polyglot…多言語対応
    Vert.xはJavaで作られていますが、Vert.xを使用するのにJavaを使う必要はありません。JavaやGroovyのようなJVMオペレーションに基づいた言語は当然ですが、Ruby, Python, JavaScriptなども使用することができます。JavaScriptを使用してサーバアプリケーションを構築する場合はNode.jsが競合になります。また、ScalaClojureのサポートも計画されています。
  • スーパーシンプルな並行実行モデル
    Vert.xを使ってサーバアプリケーションを構築する場合、単一スレッド・アプリケーションとしてコードを書くことができます。この事が意味するのは、同期やロック、volatile指定を使わなくてもマルチスレッド・プログラミングと同等の結果を得られるということです。
    Node.jsでは、JavaScript実行エンジンはマルチスレッドをサポートしないため、CPUコアをすべて利用するためには、複数の同じJavaScriptプログラムを実行しなければなりませんでした。しかしながら、Vert.xでは、1つのプロセス上でもCPUコア数に基づいた複数のスレッドを作成することができます。Vert.xでは、開発者がビジネス・ロジックの実装に集中できるようにマルチスレッディングを扱います。
  • イベント・バスの提供
    導入部で述べた様に、Vert.xの目標は単に「1つのサーバ処理デーモン」を開発することではありません。Vert.xは、相互に通信しあって動作する、Vert.xで構築された多様なサーバプログラム群を開発することを目標とします。このためVert.xはイベント・バスを提供し、ポイント・ツー・ポイント、あるいはPub/SubのようなMQ機能を使うことができます(イベント・バス機能を提供するために、Vert.xはインメモリ・データ・グリッドであるHazelcastmを使用します)。このイベント・バスを使い、異なる言語で構築されたサーバアプリケーション同士が容易に相互通信することができます。
  • モジュール・システムと公式モジュール・リポジトリ
    Vert.xはモジュール・システムを持っています。このモジュール・システムは一種のコンポーネントと思えば良いでしょう。つまり、Vert.xで構築したサーバアプリケーション・プロジェクトは、それ自身がモジュールになります。モジュールシステムの目的は再利用です。モジュールは公式モジュール・リポジトリに登録することができ、他者と共有することができます。

NettyとVert.xの関係、および違いは?

Vert.xの性能について議論する前に、NettyとVert.xの関係をまとめておくと、それは「Vert.xはNettyを使用する」ということです。言いかえれば、Vert.xのIOはすべてNettyによって処理されます。したがって、Vert.xとNettyのパフォーマンスの差異を検証することに意味はありません。

Vert.xは、Nettyとは異なる独立したAPIおよび機能を提供するサーバ・フレームワークであり、またNettyとは異なる目的で設計されています。Nettyは低レベルIOを処理するフレームワークであり、Vert.xはNettyよりも高いレベルのIOを処理します。

Node.jsとのパフォーマンスの比較

Vert.xによって提供される機能はNode.jsのものとは異なっているとしても、パフォーマンスの比較は重要です。以下の図1および図2は、Vert.x(JavaRuby、Groovy)とNode.jsの性能を示しています(ソースは: http://vertxproject.wordpress.com/2012/05/09/vert-x-vs-node-js-simple-http-benchmarks/)

図1は、HTTPサーバを構築し、200/OKレスポンスだけを返す場合のパフォーマンスの比較で、図2は、72バイトの静的なHTMLファイルがレスポンスとして返される場合のパフォーマンスの比較を示しています。

図1: 200/OKレスポンスだけが返された場合のパフォーマンスの比較

図2:72バイトのスタティックなHTMLファイルが返される場合のパフォーマンスの比較

上記のパフォーマンス比較はVert.x開発者によって提示されたものであり、テストは厳密な環境の下では行なわれていません。相対的な差異だけを見てください。

一点、注目すべきポイントとしては、Vert.xでのJavaScriptの性能がNode.jsよりも良いということです。とはいえ、この性能比較が信頼できるとしても、このことだけをもってして「Vert.xがNode.jsより良い」と言うのは難しいでしょう。Node.jsはSocket.ioのような素晴しいモデルを提供しており、参考資料も充実しているからです。

Vert.x用語集

Vert.xは独自の用語を定義したり、いくつかの一般的な用語をVert.x用に再定義しています。したがって、Vert.xを理解するためには、Vert.xで定義された用語を理解することが必要です。下記はVert.xを使う上で頻繁に使う用語です:

Verticle
Javaではmainメソッドを持ったクラスです。Verticleは、mainからさらに参照されるスクリプトを含んでいるかもしれません。さらにjarファイルやリソースを含んでいるかもしれません。アプリケーションは1つもしくはイベントバスで通信し合う複数のVerticleから構成されます。Javaに関しては、独立して実行可能なクラスあるいはJavaファイルとして理解することができます。
Vert.xインスタンス
VerticleはVert.xインスタンス内で実行されます。また、Vert.xインスタンスJVMインスタンス中で実行されます。したがって、単一のVert.xインスタンス中で多くのVerticlesを同時に実行することができます。Verticleは、それぞれ、自分自身のユニークなクラス・ローダーを持つことができます。こうすることで、staticメンバーおよびグローバル変数を通じたVerticles間の直接アクセスを防ぐことができます。多くのVerticleは、ネットワーク上の複数のホスト中で同時に実行することができます。また、Vert.xインスタンスはイベント・バスによってクラスタ化することができます。
並行処理
Verticleインスタンスは、常に同一のスレッド上で実行されることが保証されます。すべてのコードはシングルスレッド操作として開発することができ、Vert.xでの開発は容易になります。加えて、競合条件やデッドロックを防止します。
イベントベースのプログラミング・モデル
Node.jsフレームワークのように、Vert.xはイベントベースのプログラミング・モデルを提供します。Vert.xを使用してサーバをプログラムする場合、開発のためのほとんどのコードはイベントハンドラと関係があります。例えば、イベントハンドラを、TCPソケットからデータを受け取るように準備すると、それはデータ受信時に呼び出されます。さらに、イベントハンドラは「イベント・バスがメッセージを受け取った」「HTTPメッセージを受信した」「接続が切られた」「タイマーがタイムアウトした」などの通知を受け取ることができます。
イベントループ
Vert.xインスタンスは内部でスレッド・プールを管理しています。Vert.xは、スレッド・プールの数をCPUコア数になるべくぴったりと一致させます。スレッドはそれぞれイベント・ループを実行します。イベントループの実行中には、イベントの有無を確認します。例えば、ソケット中に読み取るべきデータがあるかどうか、イベントが発生したタイマーはどれか、などを確認します。イベントループ中で処理すべきイベントがある場合、Vert.xは対応するハンドラを呼びます(もちろんハンドラの処理期間が長すぎたりブロッキングI/Oがあったりした場合、追加処理が必要になります)。
メッセージパッシング
Verticlesはイベント・バスを通信に使用します。Verticleをアクターと仮定すると、メッセージパッシングはErlangで採用されていることで有名なアクターモデルに似ています。Vert.xサーバ・インスタンスは多くのVerticleインスタンスを持っており、インスタンス間でメッセージパッシングを行うことができます。したがって、システムはマルチスレッド配下でVerticleコードを実行することなく、使用可能なコア数を拡張することができます。
共有データ
メッセージパッシングは非常に有用です。しかしながら、それは必ずしもすべてのタイプのアプリケーション並列状況に対して最適なアプローチだとは限りません。キャッシュは、最もポピュラーなそういう例の1つです。1つのVerticleだけがキャッシュを保持しているのは非能率的です。他のVerticle群がキャッシュを必要とする場合、Verticleはそれぞれ同じキャッシュ・データを管理するべきです。したがって、Vert.xはグローバルなアクセス方法である共有マップを提供します。Verticlesは不変のデータだけを共有します。
Vert.xコア
その名の通り、これはVert.xの中核機能です。Verticleが直接呼ぶことができる関数はコアに含まれています。Vert.xがサポートする各プログラミング言語APIは、コアにアクセスすることができます。
Vert.xアーキテクチャ
Vert.xの単純なアーキテクチャーは次の図3に示されます。

図3:Vert.xアーキテクチャー(出所: http://www.javacodegeeks.com/2012/07/osgi-case-study-modular-vertx.html )

Vert.xのデフォルト実行ユニットはVerticleです。また、いくつかのVerticleは、同時に1つのVert.xインスタンス上で実行することができます。Verticleはイベントループ・スレッド上で実行されます。Vert.xインスタンス群はネットワーク上の1つのホストだけでなく、複数のホスト上でも実行することができます。この際、Verticlesやモジュールはイベントバスで通信します。
まとめると、vert.xアプリケーションはVerticle群とモジュールで構成されており、それらの通信はイベント・バスで行います。

vert.xプロジェクト構造
下記は、Vert.x Githubページからコピーしたソース・コードのVert.xプロジェクト構造をExlipseで表示しているところです。


図4:Vert.xソースのツリー。


全体の構成は以下のとおりです:

  • vertx-coreはコアライブラリーです。
  • vertx-platformは分散処理とライフ・サイクルを管理します。
  • vert-langコアJava APIを言語に提供するときに使われます。

Gradleがそのプロジェクトビルドシステムとして採用されています。antとMavenよりも優れた点があります。

Vert.xのインストールおよび単純な例の実行

Vert.xは、Java 7のinvokeDynamicを使用するため、JDK7が必須です。Vert.xのインストールは容易です。

  1. http://vertx.io/downloads.htmlから任意のフォルダに圧縮したインストール・ファイルをダウンロードしてください。
  2. ファイルを解凍し、binディレクトリをPATH環境変数に加えてください。

以上がすべてです(訳注: gvm Toolを使ってもvert.xをインストール可能です)。コマンド・ウィンドウ中で、vertx versionを実行してください。バージョン情報が成功裡に表示されれば、インストールは完了です。

例1

さて、JavaScriptで「Hello World!」を表示する単純なWebサーバーを構築し実行しましょう。
次のコードをserver.jsというファイルに保存してください。
これは、Node.jsのコードとほとんど同一です。

  load('vertx.js');

  vertx.createHttpServer().requestHandler(function(req) {
  req.response.end("Hello World!");}) .listen(8080, 'localhost');

vertxコマンドを使用して、作成したserver.jsアプリケーションを実行します:

  $ vertx run server.js

ブラウザでhttp://localhost:8080を開き、「Hello World!」が表示されれば成功です。

例2

別の例を、別の言語を使ってやってみましょう。次のコードはJavaで書かれています。静的ファイルの内容を読みとって、HTTPレスポンスとして返すWebサーバーです。

  Vertx vertx = Vertx.newVertx();
  vertx.createHttpServer().requestHandler(new Handler() {
      public void handle(HttpServerRequest req) {
          String file = req.path.equals("/") ? "index.html" : req.path;
          req.response.sendFile("webroot/" + file);
      }
  }).listen(8080);

次のコードは、まったく同じ機能を持ちますがGroovyで書いた場合です:

  def vertx = Vertx.newVertx()
  vertx.createHttpServer().requestHandler { req ->
  def file = req.uri == "/" ? "index.html" : req.uri
  req.response.sendFile "webroot/$file"
  }.listen(8080)

Vert.xとNHNの将来

NHN社において我々はVert.xを高く評価しており、公式リリース以前からVert.x開発に注目しています。私たちはVert.xを改善について議論するために2012年6月以来、Vert.xの主な開発者(Tim Fox)と連絡を取っています。例えば、Vert.xの上のSocket.ioについてです。

Socket.ioはNode.jsだけで利用可能でしたが、私たちはJavaに移植し、Gitubの上のVert.xリポジトリにPull Requestを送りhttps://github.com/vert-x/vert.x/pull/320、今まさに、Vert.x-modプロジェクトにマージされるところです。私たちの努力であるsocket.io vert.xモジュールは、NHNで開発しているRTCS 2.0バージョン(vert.x+Socket.io)で使用されます。

Node.jsの人気の理由の一つは、Socket.ioのためであるかもしれず、Vert.xでもSocket.ioを使用できるようになれば、Vert.xの用途は広がるかもしれません。更に、このsocket.io vertxモジュールを埋め込みライブラリとして使用することで、Javaベースのアプリケーション中でsocket.ioを使用できるようになります。

RTCSって何

RTCS(瞬時通信システム)はNHNによって作られたリアル・タイム・ウェブ開発プラットフォームであり、ブラウザとサーバ間のメッセージのリアル・タイム転送を支援します。
RTCSはBaseball 9、Me2Dayチャット、BANDチャットのようなNHNウェブサービスのために使われています。

まとめ

Vert.xの最初のバージョンは2012年5月にリリースされたので、2009年に最初のバージョンがリリースされたNode.jsと比較すると、Vert.xの歴史は非常に短く、参考資料は多くありません。しかし、Vert.xはVMwareに支援されており、CloudFoundryの上で動作することもできます。なので、多くの参考資料がすぐに入手可能になるだろうと期待しています。

著者

Web Platform Development Labのソフトウェア・エンジニア、Seongmin Wooによる。

参照