2013年12月9日月曜日

WildFly の Web 基盤、Undertow の紹介

この記事は JavaEE Advent Calendar 2013 の 12/9 の記事です。

昨日は @n_agetsu さんの CDIでアプリケーション設定をインジェクション でした。
明日は @sk44_ さんの JSF で日本語ファイル名のファイルダウンロード? です。

このエントリでは、WildFly の Web コンテナである、Undertow のご紹介をしたいと思います。

WildFly って何?

WildFly は、OSS の Java EE アプリケーションサーバです。2013-12-09 時点でのリリースバージョンは 8.0.0.Beta1 であり、Java EE 7 の仕様がひと通り実装されています。
以前は JBoss Application Server(JBoss AS) と呼ばれていたものですが、商用サポート版である JBoss Enterprise Application Platform(JBoss EAP)と区別がつきづらいことや、JBoss を冠するプロダクトが複数あることから、アプリケーションサーバのランタイム固有の名称として WilfFly と改名されました。JBoss AS としての最後のメジャーバージョンが 7 であったため、WildFly はその数字を引き継いで 8 から始まっていて、基本的なアーキテクチャはそのまま踏襲されています。

その他 WildFly については @nekop さんの JBoss / WildFly (全部俺) Advent Calendar 2013 をご参考ください。全部俺て。

Undertow って何?

今回の本題の Undertow ですが、WildFly の Web サブシステムです。JBoss AS 7 から、Web や EJB、データソースなどはサブシステムという単位で設定を行うようになっています。JBoss AS 7 では 7 より前の JBoss の通り、Web サブシステムは JBossWeb という Tomcat ベースの Web/Servlet コンテナ実装を利用していました。WildFly では新たに Undertow を Web サブシステムの実装に利用しています。また、設定上も Web サブシステムから Undertow サブシステムと名称が変わっています。

Undertow の特徴

Undertow は Java で実装されたフレキシブルで高パフォーマンスな Web サーバであり、NIO ベースのブロッキング/ノンブロッキング API を提供します。 
Undertow はコンポジションベースのアーキテクチャを持ち、小さく単一な用途のハンドラと結びつけることで Web サーバを構築できます。このことにより、完全な Java EE の Servlet 3.1 コンテナ、低レベルなノンブロッキングハンドラ、またはその中間の何か、といった選択ができるような柔軟性を持っています。
Undertow はビルダ API を利用することで簡単に組み込めるよう設計されています。Undertow のライフサイクルは組込先のアプリケーションで完全にコントロールすることができます。
Undertow は JBoss によりスポンサードされており、WildFly アプリケーションサーバのデフォルトの Web サーバです。
ノンブロッキング I/O ということで、よりスケーラブルになっています。また、簡単に Web サーバを構築でき、ビルダ API を利用することでアプリケーションへの組み込みも簡単とのこと。Jetty のような使い方もできそうですね。
次に Why Undertow ということで特徴をまとめています。

  • 軽量
    • Undertow は軽量で、Undertow Core の jar のサイズは 1MB 以下です。ランタイムとしても軽量であり、シンプルな埋込みサーバであれば、ヒープサイズは 4MB 以下で動きます。
  • HTTP Upgrade のサポート
    • HTTP Upgrade をサポートしており、複数のプロトコルを HTTP ポートを介して利用することができます。
  • Web Socket のサポート
    • JSR-356 を含む、Web Socket をフルサポートしています。
  • Servlet 3.1
    • 埋込み Servlet を含む、Servlet 3.1 をサポートしています。また、同じデプロイ資材に対して Servlet とネイティブな Undertow のノンブロッキングハンドラを組み合わせることも可能です。
  • 埋め込み可能
    • 少ないコード量でアプリケーションに Undertow を埋め込むこともできますし、スタンドアロン実行することも可能です。
  • 柔軟性
    • Undertow サーバはハンドラにより設定が変更可能です。必要な機能を任意に追加可能で、利用しないものに対して注意を払う必要がありません。
HTTP Upgrade をサポートしており、このおかげで WildFly では最終的に HTTP の 8080 と管理用の 9990 の 2 つのポートのみしか利用しなくなる予定[1]です。EJB や JMS などで利用するプロトコルも、HTTP Upgrade されて利用されます。

[1]https://community.jboss.org/wiki/WildFly800Beta1ReleaseNotes

トップページの最後に Show me the code として、Hello World を返すだけの、しかし非同期 IO であるサーバを実行するコード例がありますので、試してみましょう。

以下にサンプルを作っています。

https://github.com/emag/undertow-practice

依存ライブラリは、undertow-core のみです。

  io.undertow
  undertow-core
  1.0.0.Beta25


サーバのコードとして、以下を作成します。
public class HelloWorldServer {

    public static void main(String[] args) {

        Undertow server = Undertow.builder()
            .addListener(8080, "localhost")
            .setHandler(new HttpHandler() {
                @Override
                public void handleRequest(final HttpServerExchange exchange) throws Exception {

                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                    exchange.getResponseSender().send("Hello World");

                }
            }).build();
        server.start();
        System.out.println("HelloWorldServer is running!");
    }

}

非常にわかりやすいですね。localhost:8080 でリスンし、何をするかをハンドラとして設定します。ここでは Content-Type: text/plain として Hello World という内容のレスポンスを返します。
では、実際に実行してみます。
$ git clone https://github.com/emag/undertow-practice
$ cd undertow-practice
$ mvn clean compile exec:java -Dexec.mainClass=org.emamotor.undertow.practice.HelloWorldServer
[...]
HelloWorldServer is running!

サーバが立ち上がりました。リクエストしてみます。
$ curl localhost:8080 -v                           
* About to connect() to localhost port 8080 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Type: text/plain
< Content-Length: 11
< 
* Connection #0 to host localhost left intact
Hello World%

おお、ちゃんと Hello World が返ってきています。Hello World しか返せませんが、これは確かに Web サーバです。API もわかりやすいですし、10数行程度でこれはすごいですね。残念ながら自分の環境では非同期 I/O なのかを確認するには至っていないので、いずれ高負荷時にどうなるのか確認してみたいです。
また、興味がある方は main メソッドからデバッグしてみるのも良いかと思います。依存関係からもわかるように、Undertow は XNIO という JBoss プロジェクトの 1 つを基盤として利用して I/O を処理しています。XNIO は NIO を補完するような低レイヤの API を提供しているようです。なんだか難しいフレームワークのようで、がんばって理解したいところです。。

この他にも、Undertow のリポジトリにサンプルが何種類か用意されていますので、お試しいただければと思います。Undertow 1台をリバースプロキシにして、他の Undertow サーバ 3 台をロードバランスする例などもあり、おもしろいですね。定番の WebSocket による Chat サンプルもあります。

Undertow Examples

また、Undertow にはいくつかのドキュメントもあります。いくつかは本ブログで和訳もしているので、ご参考いただければと思います。

http://undertow.io/documentation/index.html

WildFly と Undertow

ここまでは Undertow 単体の内容を見てきましたが、実際には Undertow 単体というよりは、WildFly の Web コンテナとして利用が主かと思いますので、最後にいくつか設定を見ていきたいと思います。

まず、以下より WildFly をダウンロードします。

http://wildfly.org/downloads/

現在(2013-12-09)時点では 8.0.0.Beta1 が最新版です。ダウンロードした wildfly-8.0.0.Beta1.zip を適当なディレクトリに展開すればインストール完了です。

 以下を実行し、WildFly をスタンドアロンモードで起動します。標準出力に起動ログが出力されますが、以下では Undertow に関連してそうなところのみ抜き出しました。
$ cd <WildFly のインストールディレクトリ>/bin
$ ./standalone.sh 
=========================================================================

  JBoss Bootstrap Environment

  JBOSS_HOME: /home/wildfly/wildfly-8.0

  JAVA: java

  JAVA_OPTS:  -server -XX:+UseCompressedOops -Xms1g -Xmx1g -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true

=========================================================================

[...]
06:12:02,534 INFO  [org.jboss.as] (MSC service thread 1-6) JBAS015899: WildFly 8.0.0.Beta1 "WildFly" starting
[...]
06:12:03,319 INFO  [org.xnio] (MSC service thread 1-10) XNIO version 3.1.0.CR7
06:12:03,325 INFO  [org.xnio.nio] (MSC service thread 1-10) XNIO NIO Implementation Version 3.1.0.CR7
[...]
06:12:03,425 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-2) JBAS017502: Undertow 1.0.0.Beta17 starting
06:12:03,431 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 49) JBAS017502: Undertow 1.0.0.Beta17 starting
[...]
06:12:03,529 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-15) JBAS017525: Started server default-server.
06:12:03,549 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 49) JBAS017527: Creating file handler for path /home/wildfly/wildfly-8.0/welcome-content
06:12:03,561 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-13) JBAS017531: Host default-host starting
06:12:03,591 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-11) JBAS017519: Undertow HTTP listener default listening on /127.0.0.1:8080
[...]
06:12:03,867 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.0.0.Beta1 "WildFly" started in 1856ms - Started 183 of 220 services (63 services are lazy, passive or on-demand)

WildFly が立ち上がったら、管理 CLI を起動します。プロンプトが変われば管理対象サーバ(localhost:9990)に接続できています。
$ cd <WildFly のインストールディレクトリ>/bin
$ ./jboss-cli.sh -c
[standalone@localhost:9990 /]

余談ですが、JBoss AS7 では CLI 接続に 9999 ポートを指定していましたが、上述したポート削減のため 9990(http-remoting) をデフォルトで利用するようになっています。なお、Beta1 の時点ではまだ 9999 でも接続することが可能です。9999 ポートを利用したい場合は以下のように、明示的にプロトコル(remote)を指定する必要があります。
$ ./jboss-cli.sh -c --controller=remote://localhost:9999
[standalone@localhost:9999 /]

全体設定


CLI に接続し、以下コマンドを実行します。
[standalone@localhost:9990 /] /subsystem=undertow:read-resource
{
    "outcome" => "success",
    "result" => {
        "default-server" => "default-server",
        "default-servlet-container" => "default",
        "default-virtual-host" => "default-host",
        "instance-id" => undefined,
        "buffer-cache" => {"default" => undefined},
        "configuration" => {
            "filter" => undefined,
            "handler" => undefined
        },
        "error-page" => undefined,
        "server" => {"default-server" => undefined},
        "servlet-container" => {"default" => undefined}
    }
}

以前の AS7 までの web サブシステムと、設定項目が変わっています。WildFly がベースになるであろう EAP 7 を利用する場合は設定の見直しが必要になると考えられます。
なお、AS 7.1.1.Final の web サブシステムは以下です。
[standalone@localhost:9999 /] /subsystem=web:read-resource
{
    "outcome" => "success",
    "result" => {
        "default-virtual-server" => "default-host",
        "instance-id" => undefined,
        "native" => "false",
        "configuration" => {
            "container" => undefined,
            "static-resources" => undefined,
            "jsp-configuration" => undefined
        },
        "connector" => {"http" => undefined},
        "virtual-server" => {"default-host" => undefined}
    }
}

また、先日リリースされた商用版である EAP 6.2.0(AS 7.3.0.Final-redhat-14)では以下です。
[standalone@localhost:9999 /] /subsystem=web:read-resource                
{
    "outcome" => "success",
    "result" => {
        "default-virtual-server" => "default-host",
        "instance-id" => undefined,
        "native" => false,
        "configuration" => {
            "container" => undefined,
            "static-resources" => undefined,
            "jsp-configuration" => undefined
        },
        "connector" => {"http" => undefined},
        "valve" => undefined,
        "virtual-server" => {"default-host" => undefined}
    }
}

valve 設定が増えていますね。

Handler


Undertow サブシステムには Filter や Handler という見慣れない設定が存在しています。ここでは Handler について少しだけ見てみます。
[standalone@localhost:9990 /] /subsystem=undertow/configuration=handler/
file           reverse-proxy

handler として file と reverse-proxy という設定が用意されています。file では localhost:8080 でアクセスしたときのウェルカムファイルの設定が行われています。
変わり種は reverse-proxy ですね。Undertow 単体で reverse-proxy の設定ができるようになっています。今後別エントリで試してみたいと思います。すぐに試してみたい方は、以下のビデオで設定が載っていましたので、ご覧ください。

  • Dive Into WildFly 8
    • Reverse Proxy については 45:05 くらいから言及されています。

アクセスログの設定


アクセスログの設定も以前とパラメータが変わっています。設定後は反映に再起動が必要です。
[standalone@localhost:9990 /] /subsystem=undertow/server=default-server/host=default-host/setting=access-log:add
[standalone@localhost:9990 /] /subsystem=undertow/server=default-server/host=default-host/setting=access-log:read-resource
{
    "outcome" => "success",
    "result" => {
        "directory" => expression "${jboss.server.log.dir}",
        "pattern" => "common",
        "prefix" => "access_log",
        "rotate" => true,
        "worker" => "default"
    }
}
[standalone@localhost:9990 /] :reload

最後に

Java EE アプリケーションサーバにとって、Web コンテナはその根幹をなす非常に大切なものです。今まで JBoss AS は、Tomcat という伝統ある Web コンテナをベースにしてきましたが、WildFly にて刷新されました。刷新の理由はノンブロッキング API であったり、HTTP Upgrade のためだったりと様々ですが、近年の Web に対応するために色々な需要を取り込んでいることが伺えます。
今後、WildFly が正式リリースされるまでにどう仕上がっていくか楽しみですね。

参考

  • WildFly 8 Webinar Recording
    • Undertow について、30:05 くらいからスペックリードの  Stuart Douglas 氏が解説しています。

2 件のコメント:

  1. WildFly1.0でstandalone.xmlの中にUTF-8エンコーディングの指定をいれたのですが、全く効きません。もしかしてUndertowに何か設定が必要なのでしょうか。ご存じならお教えください。

    返信削除
  2. デプロイされるアプリケーションのエンコーディングの指定、ということでしょうか。でしたら、war 中に以下参考ページのような jboss-web.xml を含めることで設定できるかと思います。

    ■WildFly - WildFly8.0.0CR1でPOSTパラメータの文字化けを回避する
    http://www.nailedtothex.org/articles/wildfly/postutf8/

    返信削除