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 氏が解説しています。

2013年12月1日日曜日

WildFly に Byteman をアタッチさせる

WildFly に Byteman をアタッチさせる手順メモ。
環境はスタンドアロンを想定しています。

Byteman のダウンロードとインストール

個人的な趣味で恐縮ですが /opt 以下にインストールし、byteman とエイリアスをさせます。ダウンロードする URL やファイル名が変わらない限りは、BYTEMAN_VERSION を変更するだけで動作すると思います。
BYTEMAN_VERSION="2.1.3"
BYTEMAN_BINARY="byteman-download-$BYTEMAN_VERSION"
BYTEMAN_DOWNLOAD_URL="http://downloads.jboss.org/byteman/$BYTEMAN_VERSION/byteman-download-$BYTEMAN_VERSION-bin.zip"
cd /opt
wget -qO- -O tmp.zip $BYTEMAN_DOWNLOAD_URL && unzip tmp.zip && rm -f tmp.zip && ln -s $BYTEMAN_BINARY byteman

WidlFly 起動時に Byteman をアタッチ

以下の JVM 起動オプションを <WildFly のインストールディレクトリ>/bin/standalone.conf に追記します。
BYTEMAN_HOME=/opt/byteman
BYTEMAN_RULE=/path/to/somerule.btm
BYTEMAN_OPTS="-javaagent:$BYTEMAN_HOME/lib/byteman.jar=listener:true,boot:$BYTEMAN_HOME/lib/byteman.jar"
if [ "x$BYTEMAN_RULE" != "x" ]; then
  BYTEMAN_OPTS="${BYTEMAN_OPTS},script:$BYTEMAN_RULE"
fi
BYTEMAN_OPTS="$BYTEMAN_OPTS -Dorg.jboss.byteman.transform.all
-Dorg.jboss.byteman.debug"
JAVA_OPTS="$BYTEMAN_OPTS $JAVA_OPTS"
後は BYTEMAN_RULE で指定しているパスを適宜適用したいルールのファイルを設定すれば完了です。試しに WildFly の起動/終了時にログを出させるルールを wildfly-begin-end.btm 等として適用してみます。ルールの内容は以下です。WildFly の起動と停止メソッドの終了に合わせてログを出力しています。

起動および停止を行うと、以下のようなログが出力されます。
# 起動時
[...] ...
[...] INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.0.0.Beta1 "WildFly" started in 2058ms - Started 181 of 218 services (62 services are lazy, passive or on-demand)
[...] INFO  [stdout] (Controller Boot Thread) Default helper activated
[...] INFO  [stdout] (Controller Boot Thread) Installed rule using default helper : WILDFLY_STARTED
[...] INFO  [stdout] (Controller Boot Thread) WildFly started with Byteman!

# 停止時
[...] ...
[...] INFO  [org.jboss.as] (MSC service thread 1-2) JBAS015950: WildFly 8.0.0.Beta1 "WildFly" stopped in 18ms
[...] INFO  [stdout] (MSC service thread 1-2) Installed rule using default helper : WILDFLY_STOPPED
[...] INFO  [stdout] (MSC service thread 1-2) WildFly stopped with Byteman!

参考

2013年11月24日日曜日

Undertow Handler Authors Guide 和訳

このエントリは WildFly(旧名 JBoss AS)の Web サブシステムとしても利用される Undertow の以下設計ドキュメントの和訳です。実際に存在するクラス名も出てきますので、ソースコードもお手元にあるとよいかと思います。

Undertow Handler Authors Guide

原本は https://github.com/undertow-io/undertow-io-site の eda33a 時点のものを利用しています。更新が確認され次第、随時反映します。

なお、自分が理解が足りていないところが多いため、変な日本語になっています。。ちょくちょく直していく予定です。

イントロダクション


このガイドでは、Undertow のネイティブハンドラの作成方法の概要を説明します。HttpServerExchange オブジェクトの全ての API に言及している訳ではありません。API の多くは自明であったり、javadoc で理解可能なものです。そのかわり、このガイドでは Undertow のハンドラを記述する際に必要な概念の説明を中心に行います。

シンプルな例から始めましょう。
public class HelloWorldServer {

    public static void main(final String[] args) {
        Undertow server = Undertow.builder()                                                                                //Undertow ビルダ
                .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();
    }

}
大部分は自明です。

Undertow ビルダ


この API により、Undertow の設定と起動をすぐに行うことができます。この API は組込みや試験環境での用途を想定しています。現時点で、この API は変更される可能性があります。

リスナバインディング


次の行では Undertow が localhost の 8080 ポートにバインドするようにしています。

デフォルトハンドラ


このハンドラは Undertow に登録されたパスにどれもマッチしない URL に対して割り当てられます。このケースでは他にハンドラを登録していないので、常にこのハンドラが実行されます。

レスポンスヘッダ


自明ですが、これは Content-Type ヘッダに値を設定しています。1つ注意すべきなのは、Undertow ではヘッダのマップのキー値として String 型ではなく、ケースインセンティブな文字列である io.undertow.util.HttpString を利用します。io.undertow.utils.Headers クラスは一般的なヘッダーをあらかじめ定数で定義しています。

レスポンスセンダ


Undertow のセンダ API の役割はレスポンスを送ることのみです。センダの詳細は後ほど述べますが、このケースでは完了に対応するコールバックが指定されていないので、センダは、与えられた文字列が完全なレスポンスであると認識します。そして Content-Length ヘッダの設定を行い、レスポンス処理を完了します。

今後のコード例ではハンドラ自体に焦点を当て、サーバのセットアップ部分は割愛します。

リクエストのライフサイクル


(このことはリクエストライフサイクルのドキュメントでも 取り上げています。)

クライアントがサーバへ接続する際、Undertow は io.undertow.server.HttpServerConncection を生成します。クライアントがリクエストを送ると、リクエストは Undertow のパーサによってパースされ、その結果の io.undertow.server.HttpServerExchange がルートハンドラへ渡されます。ルートハンドラの処理が完了すると、以下の4つのいずれかの状態となります。

Exchange がすでに完了している


リクエスト及びレスポンスチャンネルの完全に読込み/書込み終わっている場合、Exchange は完了したとみなされます。(GET や HEAD などで) No Content が返されるようなリクエストに対しては、リクエスト側は自動的に完全に読込み終わったと見なします。ハンドラが完全なレスポンスを書込み、クローズし、レスポンスチャネルをフラッシュすると読込み側は完了したとみなされます。Exchange がすでに完全な状態となっているならば、Exchange は完了したとして何もしません。

ルートハンドラが Exchange の完了なしに正常に返る


このケースでは Exchange は HttpServerExchange.endExchange() が呼ばれることで完了します。endExchange() のセマンティクスについては後述します。

ルートハンドラが例外を伴って返る


このケースではレスポンスコードとして 500 が設定され、Exchange はHttpServerExchange.endExchange() を使って終了します。

ルートハンドラが HttpServerExchange.dispatch() が呼ばれた後、または非同期 IO が開始された後に返る


このケースではディスパッチされるタスクがディスパッチエグゼキュータへと渡されるか、非同期 IO がリクエストまたはレスポンスチャネル上で開始していれば、それから処理が始まります。このケースでは Exchange は完了せず、いつ処理が終わって Exchange が完了するかは、非同期タスク次第です。

もっとも一般的な HttpServerExchange.dispatch() の利用用途は、ブロッキング処理の行えない IO スレッドから、ブロッキング処理の行えるワーカースレッドへ処理を委譲することです。この例は典型的には以下のようになります。

ワーカースレッドへの処理の委譲
public void handleRequest(final HttpServerExchange exchange) throws Exception {
    if (exchange.isInIoThread()) {
      exchange.dispatch(this);
      return;
    }
    // ハンドラのコード
}
Exchange は実際にはコールスタックが返るまでディスパッチされないため、ある Exchange 中において同時に 2 つ委譲のスレッドが動作することはないと考えてよいでしょう。この Exchange はスレッドセーフでないですが、複数のスレッドが同時に Exchange を変更しようとしない限り、複数スレッド間で共有される可能性があります。また、1つ目と2つ目のスレッドアクセスの間に、スレッドプールディスパッチのようなことが起こりえます。

Exchange の完了


上で説明した通り、Exchange はリクエストおよびレスポンスチャネルのクローズとフラッシュすると完了したとみなされます。

Exchange の完了には2種類あり、リクエストチャネルの読込みが完了し、レスポンスチャネルで shutdownWrites() が呼ばれ、レスポンス内容がフラッシュされたときか、HttpServerExchange.endExchange() が呼ばれた時です。endExchange() が呼ばれた時、Undertow はコンテンツが生成されたかどうかチェックします。生成されていればリクエストチャネルを開放し、レスポンスチャネルのクローズとフラッシュを行います。コンテンツが生成されていない場合、Exchange にデフォルトのレスポンスリスナが登録されていれば、Undertow はレスポンスリスナにデフォルトのレスポンスを設定します。こうしてデフォルトのエラーページを返す機構を提供します。

Undertow のバッファプール


Undertow は NIO をベースとしているので、バッファリングが必要な場合は java.nio.ByteBuffer が使われます。これらのバッファは要求ごとに割り当てると性能に重大な影響を与えるため、プールされます。このバッファプールは HttpServerConnection.getBufferPool() を実行することで取得できます。

プールされたバッファは利用後はガベージコレクトの対象にならないため、開放されなくてはなりません。プール中のバッファのサイズはサーバが作成された際に設定されます。経験則として、最大限のパフォーマンスを出すサイズは 16kb です(これは Linux のデフォルトのソケットバッファサイズと一致します)。

ノンブロッキング IO


デフォルトでは Undertow はノンブロッキングの XNIO チャネルを利用し、リクエストは最初 XNIO の IO スレッド上で実行されます。これらのチャネルは受信データを送るために直接利用されます。これらのチャネルはかなり低レベルですが、Undertow はよい扱いやすくするために抽象化したものを提供します。

ノンブロッキング IO を利用したレスポンスを送信するもっとも簡単な方法は、上述したセンダ API を利用することです。センダ API は byte や String 両方に対応した send() メソッドを様々な用途にあった形で提供しています。メソッドの中には、送信が完了したらコールバック処理を実行するものや、コールバックをしない代わりに送信が完了した際に Exchange を終了させるものもあります。

センダ API はキューイングをサポートしないことに注意してください。send() をコールバックが通知されるまで呼ぶことはおそらくできません。

コールバックを取らない send() メソッドは、Content-Length ヘッダが自動的に設定されます。そうでなければ chunked エンコーディングを避けるために、自分で Content-Length ヘッダを設定する必要があります。

センダ API はまた、ブロッキング IO をサポートします。Exchange が HttpServerExchange.startBlocking() の実行によりブロッキングモードになった場合、センダは Exchange のアウトプットストリームを利用してデータを送信します。

ブロッキング IO


Undertow はブロッキング IO もサポートしています。XNIO のワーカスレッド中ではブロキング IO を使えないため、リード/ライトする前にリクエストをワーカスレッドプールへディスパッチする必要があります。

ワーカスレッドをディスパッチするコードは、前述のとおりです。

ブロッキング IO を利用するために HttpServerExchange.startBlocking() を実行します。このメソッドは 2 つあり、1 つは引数を取らず Undertow のデフォルトのストリーム実装を利用します。もう 1 つの HttpServerExchange.startBlocking(BlockingHttpServerExchange blockingExchange) では、利用中のストリームをカスタマイズして使うことができます。例えば サーブレット実装では Undertow のデフォルトストリームの代わりに Servlet(Input/Output)Stream を使うためにこの 2つ目のメソッドを利用しています。

ブロッキングモード Exchange が完了すると HttpServerExchnage.getInputStream() と HttpServerExchange.getOutputStream() を実行することができ、通常通りデータを書き込むことができます。上で述べた sender API も利用できますが、この場合 sender の実装はブロッキング IO が利用されます。

デフォルトで Undertow はバッファリングストリームを利用し、バッファはバッファプールから取り出されます。レスポンスがバッファに適合するほど十分小さければ、Content-Length ヘッダが自動的に設定されます。

ヘッダ


リクエストとレスポンスヘッダは HttpServerExchange.getRequestHeaders() と HttpServerExchange.getResponseHeaders() を介してアクセスできます。これらのメソッドは最適化sれたマップ実装である HeaderMap を返します。

ヘッダは最初のデータがチャネルへ書き込まれたときに HTTP レスポンスヘッダに書きだされます(バッファリングされていた場合、最初にデータが書き込まれた時刻とは一致しない可能性があります)。

ヘッダへの書き込みを矯正したい場合は、レスポンスチャネルまたはストリームで flush() メソッドを利用できます。

HTTP Upgrade


HTTP upgrade を実行するために HttpServerExchange.upgradeChannel(ExchangeComplietionListener upgradeComplitionListern) が利用できます。レスポンスコードは 101 に設定され、Exchange が完了するとリスナに通知されます。ハンドラは upgrade クライアントが期待するような適切なヘッダを設定することが可能です。


2013年11月10日日曜日

JJUG CCC 2013 Fall メモ

JJUG CCC 2013 Fall に参加しました。どのセッションも非常におもしろく、あっという間に1日が過ぎてしまいました。

WebSocket のハンズオンは社内システムに応用できそうなアーキテクチャ(WebSocket + JMS)なので、ぜひ WildFLy 上で動作するようにしてみたいと思います。

JVM コードリーディング入門は、大規模ソースコードのリーディングに普遍的に通用するテクニックを教えていただきました。めげずにいろいろと読んで行こうと思います。

Virt.x も気になるプロダクトです。まずは Acroquest さんのブログで入門したいと思います。

以下、メモをはりつけ(手抜きですみません)。発表スライドは公表されたら順次リンクしていきます。

参加セッションリスト



K-2 2013 エンタープライズ Java 最前線

GlassFish のロードマップ


  • 商用ライセンスサポートはバージョン 3.1.2.2 までで終了
  • バージョン 4 以降は OSS 版のみ提供
  • 今後 Oracle が提供する Java アプリケーションサーバは WLS のみ
  • 今後も Java EE の RI であるという GlassFIsh の位置づけは変わらない

Java EE 7 2013.6 リリース


  • EE 7 は EE 6 を拡張したもの。Web のトレンド(HTML5, WebSocket など)に対応したもの。
    • EE 6 は EoD の集大成と言えるリリースであった。
  • WebSocket の注目が高い(寺田さん調べ)
  • CDI + JTA で EJB をかなり置き換えられるようになった。
  • Bean Validation がいろいろなところで使えるようになった。
  • Concurancy Utility で、ユーザアプリケーション側でスレッドを起こすことが可能になった。
  • JAX-RS が Full Profile から Web Profile へ移った。

Project Avatar


  • JDK8_b103 以降で利用できる。 
  • 今年の Java One で OSS 化された旨が発表された。
  • Avatar を試してみたい場合、自力でセットアップするよりも、Avatar 公式サイトからダウンロードできる Avatar が同梱された GlassFish を使うのがおすすめ(どこにあるかわからない...)。 
  • Avatar は JS のエンジン(Nashorn)上で動作する。
    • Nashorn は JVM 上で動作する JS エンジンで、JDK8 の 1 プロジェクト。旧来の JS エンジンである Rhino より性能がよいらしい。 
      • 性能が良いのは、JDK8 における Invoke Dynamic 命令の恩恵らしい。 
  • Avatar で TSA を実現する場合。// TODO 寺田さんの絵がほしい。 
  • Avatar はデータバインディングに EL 式を使える。 
    • Avatar コンパイラでコンパイルする。 
  • Avatar でビルドしたアプリケーションは、GlassFish にそのままデプロイできる。 
  • 同じ AP サーバ上にほかの Java EE アプリケーションもデプロイされるので、Java <-> JS の相互呼び出しが可能。 
  • Avatar のサンプルアプリケーションは寺田さんのブログを参照。

Java EE 8 とその将来


  • まだまだ決まっていないことがたくさんあって、これからの状態
    • JSON バインディング、JCache は確実に入ってきそう。

R5-1 Java EE 7 WebSocket ハンズオン

// TODO WIldFLy で動かせるようにしたい!

  • 作成するアプリケーションは WebSocket による大規模リアルタイム情報配信
    • チャットとかだとバックエンドのサーバがあまり企業システムらしくないので、それらしいものを作る。
    • 利用テクノロジは WebSocket + JSF + JMS WebSocket
    • サーバはクラスタ構成にする。
    • JMS アプリケーションと WS アプリケーションは別のアプリケーションとして提供する。
  • 寺田さんは public フィールドはお好みでないようだ。
  • JSF のエッセンスは ManagedBean と EL 式。
  • Java EE 7 の WebSocket API は簡単に使えるように設計されている。
    • POJO
    • ポイントになるアノテーションは以下
      • @ServerEndpoint
      • @OnMessage
      • @OnOpen
      • @OnClose
      • etc.
    • Session#getOpenSessions で接続してきているすべてのクライアント情報を取得できる。 
  • GlassFish の JMS 実装は OpenMQ
  • JMS2.0 かんたん!
  • 途中で出てきた 4001 ってなに?


H-3 ユニットテスト改善ガイド


  • ユニットテストを組織に広めるためのヒントを紹介
  • (参考)渡辺さんのクラスメソッドのブログ記事
    • JUnit はあくまでテストするためのツールであり、「JUnit を使うこと」が目的ではない
    • ユニットテストを作るにはそれ相応のスキルが必要である
    • etc.
  • ユニットテストは適材適所であるべき
    • 全てやる派 vs 薄くやる派
  • テストは誰でもできる簡単なものではなく、トレーンングが必要
    • テスティングフレームワークの使い方やテスト手法を学ぶ
    • プロジェクトのコストとして扱わず、業務時間にトレーニングを実施する。
    • コーチができる人を探す。
  • テストが難しいコード、簡単なコードがある。いきなりすべてをやろうとせず、できるところからやる。
  • テストがしにくいAPI=使いにくいAPI。
    • テスト対象を直してはならないという勘違い。
    • 一度書いたものは変えたくないという抵抗感を減らす1つの方法は、テストファーストを行うこと。
  • CI を導入する前に、テスト文化を根付かせるのが先。
    • CI 導入にもノウハウが必要。一緒にテストも学ぶのは大変。
  • CI の効果
    • リグレッションをすぐに検知できる
    • 自動化が「見える」
      • モチベーションが上がる
    • 見える化のおかげで、管理者にも説明しやすい。
  • テストコード自体のメンテナンス・リファクタリングが必要。
    • テストは使い捨てでない
    • DRY をサボると確実にコピペ地獄になる。
    • 動かないテストは思い切って捨てるべき
    • テストは常にきれいにしておくこと。
      • ただし、整理しすぎるとかえって可読性が落ちることがあるので、あくまでテストとして読みやすいバランスを保つこと。
  • ユニットテストの効果を伝えずに押し付けても反発されるだけ。
    • メンバの手間を増やさない
    • 導入する目的を説明する
    • デバッガの代わりに紹介してみる
  • 上司の説得方法

R2-4 Javaアプリケーションサーバ構築・運用の勘所

ログ管理


  • トラブルシューティングの基本
    • GC ログ
      • 取っていないケースも多い。
      • 標準出力に出すのもあり
        • 普通の出力と混じってしまうのでは?
    • スレッドダンプ
      • ThreadLogic
        • WLS に特化した機能も含むものの、ほかの製品でも使える。
        • 侍のほうが直感的な UI だが、ThreadLogic は診断結果などが出
        • 大きなスレッドダンプも読み込み可能。
    • アクセスログ
      • JBoss/Tomcat はデフォルトで出るようになってほしい。
      • 性能劣化している時の切り分けとして、前段の Apache のレスポンスタイムと比べる。
    • CPU
      • ps auxwww -L プロセス単位でなく、スレッドごとのCPU使用率を知りたい。
      • コネクションプール不要論に対する反論
        • プールするのは性能だけでなく、リソースを制限することにも意味がある。

R5-5 JVMコードリーディング入門 ~JVMのOS抽象化レイヤーについて

対象のソースコード


  • jdk7u

読む前の準備


  • ビルドするかしないか
    • デバッグ実行しながら動作を調べるなら必要
    • 動かす前の調査としてコードを読むなら不要

JVM を読む基本的な方法


  • わからなくても全部をざっくり何週も読む
  • メモ・図解をする
    • わかったこともわからないこともメモしないと忘れてしまう。
  • 名言「理解とは真実との相対的な距離を縮める行為である。」
    • 仮設と懸賞を繰り返す。
  • 鳥の目と虫の目を交互に繰り返すことでわかることもある
  • ファイルサイズの大きい物を把握する
    • 重要 or ゴミの可能性大
  • 抽象度の高い名前を付けられたファイルを把握する
    • より重要度が高いファイルである可能性大
  • クラスの関係性を把握する
  • いろいろなクラスから継承されていたり、フレンド関数としてアクセスされてたり
  • C, C++ のレイヤを切り分ける
    • システムコールを行う層とそれを使う層
    • OS との境界
  • ざっくり読む
    • 読む部分と読み飛ばしても良い部分
      • assert -> 読む。なんらかの重要なチェックが行われている
      • non-PRODUCT -> 読み飛ばして OK。デバッグ情報など。
  • ソースコードのコメントをどう読むか
    • コード中のものはメンテされていない可能性もあるため、完全に信頼は置けない。
    • ヘッダファイルは基本的に読む。設計指針や専門用語の解説あり
    • ソース中でも以下のコメントは読む
      • 署名がついているもの
      • バグトラックの番号がついているもの。セキュリティホール対応だったりする。
      • 日付が書いてあるもの
    • JVM の抽象クラスの特性
    • メモリ周りのコードに億出てくる略語を押さえる
      • oop, RO, TLAB, BOT...
    • os <- 猫に見える

JVM の OS 抽象化レイヤ


  • memory <- 寝てる猫に見える
  • os.hpp
  • os.cpp
  • os:: を呼び出しているクラスは share/vm/memory にある
  • memory ディレクトリに含まれるクラスの継承関係を図示する。
  • allocation.hpp|cpp 中のクラスが大事そう。

R5-6 [BOF] Over the Node.js. An Introduction to Vert.x

Vert.x の概要


  • Vert.x は JVM 上で動くアプリケーションプラットフォーム。
  • ドキュメントが充実している(vertx.io)
  • Virt.x の作者 timfox は Red Hat で HornetQ とか関わっている。
  • 特徴
    • High Perf.
    • Distributed Event Bus
    • Polyglot
    • Module System
  • High Perf.
    • C10K問題。一般的に1つのスレッドあたり、256kb - 1mb 必要
    • EventLoop でノンブロッキング+非同期処理
      • EventLoop を止めてはいけない。
        • どうしてもブロックしたいときは、WorkerThread を利用する。
    • Java のスレッドを利用するので、デフォルトで CPU 数に応じたスケーリングが可能
      • node.js では、クラスタ機能を使わないとだめ。単体だと 1 CPU での動作になる。
  • Distributed Event Bus
    • JMS みたいな通信(P2P, Pub/Sub)
    • クラスタリングも可能
  • Polyglot
    • JVM 上で扱える様々な言語をサポート
    • Java JS Groovy JRuby Jython が公式でサポート
    • Scala Clojure PHP も開発中
  • Module System
    • node.js でいう npm のような機構
    • モジュールは Maven や Bintray に公開し、共有できる
    • モジュール間は Event Bus を介した疎結合
    • モジュールはいろいろな言語で実装されているが、Event Bus を介しているため、言語の差を意識しないで済む。

Vert.x による開発


  • JDK7 or later(must)
  • Project Template は、gradle を使うのがおすすめ。
  • Auto Reload
  • Remote Debug も可能
  • Node.js and the new web front-end
    • Backend は Virt.x

Vert.x の最新情報


  • Fat Jar(executable jar)
    • Virt.x Core を同梱しているので、実行に Virt.x のインストールが不要。
  • HA
    • HA Group を作る。HornetQ っぽい。
    • QUORUM スプリットブレイン対策
  • DNS / UDP Core API

Netty, Virt.x, Java EE アプリケーションサーバ

  • Netty, Virt.x, Java EE アプリケーションサーバの住み分けは?
    • Netty は低レイヤ向きのアプリケーション向け(ゲートウェイとか)
    • Virt.x は Netty より高レイヤかつ、M2M などのサービスに向いているのでは
      • あくまで LightWight を目指し、Java EE アプリケーションサーバの範囲を全て網羅するような作りにはならないように思える。

参考


R1-7 [BOF] Spring Frameworkの今(2013年版)


  • Spring は生きている。
    • ホストしている会が移り(Pivotal社)、どう扱われるかという意味で未来にはちょっと不安な部分もある。
  • spring.io にメインサイトが変更された。
    • 40 種類のサンプルを用意されている。
  • Spring Boot
    • 複雑化した Spring モジュールを簡単に扱えるようにする。
  • CDI(JSR-330)対応
  • SockJS 対応
    • レガシーブラウザでも利用できる WebSocket
  • STOMP に対応
  • Spring vs Java EE
    • Spring もどんどん JSR に対応していき、Java EE と差がなくなってきた。
    • JTA が使えるのでグローバルトランザクションもできる。
    • 外部 JMS との連携も可能。

その他


  • Tomcat 7.0.47 では JSR 標準の WebSocket API を制限付きで同梱している。
  • JSR356は低レイヤ過ぎる。なんらかのフレームワークが必要。

2013年11月9日土曜日

ブログアプリケーションを作ろう

@making さんのブログがお手製であることに衝撃を受けて、自分でも作ってみたくなりました。

リポジトリは以下です。

https://github.com/emag/morecat

アプリケーション名は "MoreCat"

名前が無いと説明に不便(おれのブログアプリとか、あの例のやつだけどみたいになっちゃう)なので、MoreCat というアプリ名にしました。こちらから拝借しました。

以下、仕様を挙げていきます。

機能

ブログシステムというよりは、WordPress に近いもの


blogger や はてなダイアリーのように、開設者個々人のブログ環境を提供するのではなく、WordPress のように、MoreCat をインストールすると 1 つのブログ環境を構築するイメージです。WordPress のデータ構造が参考になるのかな。

デフォルトの記法は Markdown


なんだかんだ Markdown がいいですよね。凝った表とかが必要な場合は、生 HTML を書いてもらうか、Redmine みたいな textile を参考に Markdown を拡張する感じでしょうか。

管理クライアントは GUI と CUI をデフォルトで用意する


ブログエントリ投稿方法として、Web ブラウザによる GUI だけでなく、CUI も用意したいと思っています。イメージ的には blogger でいう GoogleCL とか blogger.vim です。more とか cat とかで読み進められるといいなあ。

アーキテクチャと実装

コンポーネントは3つ

  • morecat-backend
    • 全ての API を提供するサーバ。MoreCat のコア。
  • morecat-frontend
    • フロントエンド UI。
  • morecat-admin
    • 管理コンソール UI。

Backbone.js + Java EE(JAX-RS, CDI, JPA)


フロントエンドは Single Page Application(Backbone.js 、たぶん Brunch を利用) を提供し、バックエンド(サーバサイド)はすべて REST API(JAX-RS を利用) のみの提供とします。細かいライブラリは、作りながら必要に応じて採用していきます。アプリケーションサーバは WildFly です。

プラグイン機構


ブログの外観テーマや便利ツールなどは、外から差し替えできて、かつ、サーバの再起動不要だと嬉しいですね。後からこういうプラグイン機構を入れ込むのは難しい気がするので、最初から考えておきたいと思います。
なお、どうやって実現するのかは全然イメージできてません!! 参考になりそうな Java アプリケーションだと、Eclipse, IntelliJ IDEA, Jenkins とかでしょうか。Eclipse は OSGi ですよね。

データベースは好みのものが選べるようにする


といっても、RDMBS のみとなるかと思いますが。。ひとまず PostgreSQL, MySQL の2つで動作確認を行おうと思います。なるべく JPA で NativeQuery を使わないようにすれば大丈夫かなあ。開発用には WildFLy 同梱の H2 を利用します。

...

とりあえずここまで。仕様が変更されたら随時更新していきます。のんびり作っていきたいと思います。

2013年10月18日金曜日

DELL XPS 15 L521X に Fedora 19 をインストール

DELL XPS 15 L521X を購入し、Fedora 19 をインストールしました。自分がハードウェアに疎いため、インストールするまでにいろいろと手間取りましたので、はまったポイントをメモ。

なお、インストールには Live DVD を使っています。

CD/DVD Drive からのブートをどうすればいいのか悩んだ


Live DVD を焼いてドライブに入れても CD/DVD Drive からのブートになりませんので、BIOS を立ち上げます。DELL のロゴが出ている間に、F2 を押すと BIOS 画面に遷移します。

Main, Advance Security, Boot, Exit とメニューが並んでいるので、Boot を選択します。UEFI BOOT になっているので、これを LEGACY BOOT に変更する必要があります(これに気がつくまで時間かかった...)。

まず、Secure Boot の項目を Disabled に変更します。次に、Boot List Option の項目を UEFI から Legacy に変更します。LEGACY BOOT -> Boot Type Order の項目で、CD/DVD-ROM Drive の位置を Hard Disk Drive より上に上げます(F6 で上がります)。F10 を押して BIOS 設定を保存・終了します。

上記ができると、無事 CD/DVD Drive からブートが行えます。

無線 LAN が有効にならなくて困った


ワイヤレスのネットワークアダプタが Killer N 1202 というちょっとあまり聞かないものなので、無線 LAN がうまく使えるかを確認のために Live DVD からブートした後もすぐにはインストールせずにいたのですが、無線 LAN が使えない状態でした。

右上の表示をクリックすると、Wi-Fi hardware disabled と表示されており、rfkill list を実施すると Hard blocked: yes となっていました。こうなってはコマンド的にどうこうじゃないようなので、Wi-Fi の On-Off 切り替えスイッチがどこかにあるのかと探したのですが見当たらなく、カタログにも特に表記がなく途方に暮れつつもしばらくググってみると、以下がヒットしました。


ここの回答に、「Fn + F2 を押しなはれ」と神の啓示があり、試しに押してみると Wi-Fi が利用できるようになりました。rfkill list 結果も Hard blocked: no となっています。わからんよ。。

と思ったら、F2 に、無線っぽいアイコンが刻印されてますし、ワイヤレスのオン/オフですよと説明書にもきちんと書いてありました。えへへ。

2013年10月3日木曜日

全 DOM 要素の数を確認する

いいこと聞いたのでメモ。

jQuery でリソース内の全 DOM 要素の数を確認したい時は、
$("*").length
でいけます。

例えば、要素数に比例して描画処理に時間がかかってることが予想されるときなどに目安になります。