Recent Posts

RSS Feeds

Atmosphere 0.1 GA リリース


Grizzly プロジェクトより派生した Atmosphere プロジェクトの
初めての成果物 (0.1 GA) がリリースされました。

Atmosphere プロジェクトでは汎用的に使える Comet フレームワークとして
AtmosphereHandler インタフェースを実装して簡単に Comet アプリケーションを
作成することができるようになります。

チャットの実装例

今までは Comet のアプリケーションを作成する場合、サーバ側の実装は
各 Web コンテナの Comet Engine の実装に応じてばらばらに実装しなければ
なりませんでしたが、このフレームワークを利用することで、一度実装した
Comet のサーバ側の実装を様々な Web コンテナ上で動作させることができるようになります。

サポートされる Web コンテナ: Tomcat,Jetty, GlassFish,Weblogic, Jersey,Grizzly
チャットアプリケーションの実装例:
Getting started with Atmosphere CPR part 1: Writing the HelloWord of Comet....a Chat application

Comet のアプリケーションに興味のある方は是非このフレームワークをお試しください。


ちょっと春らしく:


Permalink     No Comments
Track Back :


Blocking I/OとNon Blocking I/Oについて


以前、Grizzlyの概要 : C10K問題に対応するGlassFish(Grizzly) というエントリで、
Grizzly が実装している Non Blocking I/O の利点について説明しましたが、
Project Kenai 中のプロジェクト Grizzly-send file 内で過去に書いたエントリの
補足となるような良い説明資料があがっていましたので、こちらでも紹介します。

※ このエントリを読む前に是非、過去の記事をご覧ください。

マルチスレッドで実装された Webサーバ は下記のようにAcceptor スレッドとWorker スレッドの間に
Queueを置いて、処理の効率化をはかっています。


この時、仮に Worker スレッドの数が5個だった場合に8クライアントから同時にファイル取得の
要求が来た場合を想定します。



Blocking I/Oを使って実装されたマルチスレッドサーバの場合、実際に作業ができるWorker スレッドは
5つしかありませんので、3つのクライアントは他の処理が終わるまで Queue で待たなければなりません。
取得する対象のコンテンツサイズが小さい、もしくはすぐに完了するような場合、このBlocking I/O
のマルチスレッドサーバは高速に動作します。

しかし、クライアントからのリクエスト数が膨大になった場合や、取得するコンテンツのサイズが大きい場合、
もしくはコンテンツの取得に時間がかかるような場合、他のクライアントの要求に対して影響が大きいことが
わかります。




一方、Grizzly のように Non Blocking I/O に対応した Web Server を利用すると、Woker スレッドを
効率的に利用することができるようになります。



5つの Woker スレッドが8クライアントからの同時アクセスに対して、各スレッド毎で分割して
処理できますので、誰か1人が大きなファイルを取得したとしても他の人に対して影響は少なく
なります。
そこで、クライアントからのリクエスト数が増えれば増える程、Non Blocking I/Oの実装の恩恵を
受ける事ができます。

PS.
今回は Project Kenai の Grizzly-Send file の画像を使わさせて頂きました。
私が作成した概念図より各スレッド毎に分割されて処理しているのが
分かりやすいので、二つの資料を両方つかうとわかりやすいですね。

Permalink     No Comments
Track Back :


Grizzly sendfile - Project Kenai


大きなファイルを高速に配信する Grizzly の sendfile
(X-Sendfile ヘッダ)が GlassFish v2.x でも利用できます。

我々、Sun のエンジニアがプレゼンやストレーミングを公開するために
使用する、http://mediacast.sun.com でもこれが使われているのですが、
大きなファイルを効率よくクライアントへ配信したい場合は、
是非、一度試してみてください。

プロジェクトホーム:http://kenai.com/projects/grizzly-sendfile/pages/Home
参考情報:http://blog.igorminar.com/2009/02/announcing-grizzly-sendfile.html

Permalink     1 Comment
Track Back :


GlassFish で Comet のオンラインセミナー


今日の日本時間で明け方4時ころに、Grizzly の開発者である
Jeanfrancois がオンラインセミナーを開催しました。
その発表内容が下記にてストリーミングで提供されていますので、
もし、ご興味のある方は見てください。

http://www.ustream.tv/channel/theaquarium

その他にも、今日は注目の技術エントリを紹介します。

開発関連
● WS-Addressing と ステートフル Web サービス
●  RESTful Web サービス と Jersey の Client API

MySQL関連
●  Sun and MySQL: How It Stacks Up for Developers
●  GlassFish と MySQL を連携した Web サービスの構築
● Using MySQL With Java Technology

SailFin 関連
● Adding Voice to Java EE With SIP Servlets

Permalink     No Comments
Track Back :


Grizzly 1.8.0リリース


Jean-Francois Arcand's Blog
Jean-Francoisさんのブログに投稿されてますが、
先日(2008/06/11)Grizzly 1.8.0がリリースされました。

Grizzly 1.8.0は多くのバグを修正し、またパフォーマンスも
改善されております。
さらに、組み込みでOSGiに対応しています。

https://grizzly.dev.java.net/

New Features:
+ NIO.2 support (dependent on when JDK team will push the final bytes)
+ Comet implementation moved to Atmosphere.dev.java.net
+ New Error Handling mechanism
+ Improved logging framework
+ High level API for String protocol, hidding completely the needs to understand ByteBuffer.
+ Add a new TCP Proxy/load balancer module (long time requested, never completed :-))
+ i18 error messages

Bug fixes
+ Sailfin/v3/ issues
+ Users bugs issues.
+ OpenEDB integration in v3 will have started, fix their issue related to Grizzly ARP.
+ Documents fixes (approval required from the users@grizzly)

Permalink     No Comments
Track Back :


Grizzlyの概要 2 : Java New I/Oで実装されたサーバ


さてさて、今日も引き続きGrizzlyを説明したいと思いますが、
その前に、やはりJava New I/Oについて理解して頂かなければ
本当の意味でGrizzlyを理解していただけませんので、Grizzlyの
ソースコードを追う前にJava New I/Oについて簡単に復習して
おきたいと思います。
ただし、ここではほんの概要程度の説明ですのでちゃんと
理解されたい方は別途Java NIOについて説明されている記事を
読んで理解してください。

※ 本エントリの一番最後にJava New I/Oを学ぶことができる
  参考URLを示しています。

Java New I/Oでノンブロッキングサーバの実装:

前回、Java New I/Oを使って実装されたサーバは、今までの
マルチスレッド型のBlocking I/Oを使用して実装されたサーバより
新規スレッドを作成する必要がなくスレッド数を少なくすることが
できると説明しましたが、では具体的にどのようにして実装されているかを
説明致します。



まず、Non Blocking I/Oを理解する上で必要なJava New I/OのAPIを紹介します。

チャネル:

チャネルとは、ハードウェアデバイス、ファイル、ネットワークソケットのほか、
個別の入出力操作を実行できる接続を表します。
Non Blockingにできるチャネルは、java.nio.channels.SelectableChannelクラス
継承したサブクラスで、java.nio.channels.SocketChannel
java.nio.channels.ServerSocketChannelクラスがあります。
このクラスのインスタンスメソッドで、configureBlocking(false)を
実行するとチャネルをNon Blockingモードに調整することができます。

例えば、ServerSocketChannelを使ってチャネルをノンブロッキングモードに
する為には下記のようにserverChannel.configureBlocking(false)を記載します。

selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(SERVER_PORT));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0) {
    for (Iterator it = selector.selectedKeys().iterator(); it.hasNext();) {
        SelectionKey key = (SelectionKey) it.next();
        it.remove();
        if (key.isAcceptable()) {
           doAccept((ServerSocketChannel) key.channel());
        } else if (key.isReadable()) {
            SocketChannel channel = (SocketChannel)key.channel();
            doRead(channel);
       }
    }
}

serverChannelというチャネルをNon Blockingモードに設定した後、
Selectorのインスタンス selector に対してregister(登録)します。
実はこの行がJava New I/Oの中でとても重要な箇所です。

今までのマルチスレッド型のサーバの場合、java.net.ServerSocket.accept()メソッド
を呼び出してソケットのアクセプト処理を行っていましたが、Java New I/Oでは
java.nio.channels.ServerSocketChannel.accept()メソッドでアクセプト処理を行います。
この際、ServerSocketChannel.accept()メソッドはNon Blockingモードに設定されている場合、
保留されている接続がない場合、直ちにnullを返します。
つまりServerSocket.accept()のように新たなクライアントからの接続がくるまで
待ち続けることはありません。

しかし、クライアントからの接続を待たないということはサーバ側ではどのタイミングで
アクセプト処理を行えばよいのかが分からなくなります。
そこで、利用できるチャネル(接続)を取得する為にセレクタ(Selector.select())を使用します。
チャネルをセレクタに登録しておき、セレクタの中で利用できるようになったチャネルを
SelectionKeyとして取得して入出力操作を行うことができます。
セレクタには、複数のチャネルを登録することもでき、
複数の入出力操作を同時に行うことができるようになります。

例えば、下記にかんたんなエコーサーバをJava NIOを使用して
作成してみましたが、下記ではクライアントからの接続毎に
スレッドを生成していないことが御分かりいただけるかと思います。
また、マルチスレッド型のサーバのようにコネクション毎に新規スレッドを
生成していないことがわかります。

例:Java New I/Oで実装したサンプルエコーサーバ
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;

public class NonBlockingServer {

    private static final int SERVER_PORT = 8888;
    private static final int BUF_SIZE = 2000;

    private Selector selector;

    public static void main(String[] args) {
        NonBlockingServer nserver = new NonBlockingServer();
        nserver.start();
    }


    public void start(){
        ServerSocketChannel serverChannel = null;
        try {
            selector = Selector.open();
            serverChannel = ServerSocketChannel.open();
            serverChannel.configureBlocking(false);
            serverChannel.socket().bind(new InetSocketAddress(SERVER_PORT));
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (selector.select() > 0) {
                for (Iterator it = selector.selectedKeys().iterator();
                     it.hasNext();) {
                    SelectionKey key = (SelectionKey) it.next();
                    it.remove();
                    if (key.isAcceptable()) {
                        doAccept((ServerSocketChannel) key.channel());
                    } else if (key.isReadable()) {
                        SocketChannel channel = (SocketChannel)key.channel();
                        doRead(channel);
                    }
                }
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    private void doAccept(ServerSocketChannel serverChannel) {
        try {
            SocketChannel channel = serverChannel.accept();
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_READ);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    private void doRead(SocketChannel channel) {
        ByteBuffer buf = ByteBuffer.allocate(BUF_SIZE);
        Charset charset = Charset.forName("UTF-8");
        try {
            if (channel.read(buf) < 0) {
                return;
            }
            buf.flip();
            System.out.print(
                    charset.decode(buf).toString());
            buf.flip();
            channel.write(buf);
        } catch (IOException ioe) {
           ioe.printStackTrace();
        }
    }
}

このように、Java New I/Oを使用して作成されたサーバは、接続毎に
新規スレッドを作成しなくてもよくなるため、既存のサーバ実装とは
特に大量のアクセス等が発生した際に大きな差がでてきます。
例えば、メモリの消費量も少なくて済むので高負荷時になればなるほど
使用するサーバリソースが大きく変わってくるかと思います。

次回は、Grizzly 1.0.19のソースコードを読む際にどこから見ていけばよいのか、
また、Grizzly 1.0.19の中でどのようなクラスが重要なのか等
ソースコードを見るために必要な情報を紹介したいと思います。

※  Grizzlyのソースコードを読む前に、上記のSelector,SocketChannel
  ServerSocketChannel,SelectionKey等のクラスを理解しておいてください。

Java New IOの参考記事へのURL:

● 横河電機の櫻庭さんによるIT Proの記事「New I/Oで高速な入出力」
● TECHSCORE (5. ノンブロッキングチャネル)

Permalink     No Comments
Track Back :


Grizzlyの概要 : C10K問題に対応するGlassFish(Grizzly)


さて、おまたせ致しました。
本日より、JJUGで発表したGrizzlyについて
紹介したいと思います。

まず、Grizzlyって聞いたことありますか?

「GrizzlyはGlassFishコミュニティの中のサブプロジェクトの1つで
 JavaのNew I/Oを使って記述された汎用的なネットワークサーバエンジンです。」


Project Grizzly :
https://grizzly.dev.java.net/

Project Grizzlyによって開発された成果物(ネットワークサーバエンジン)は
GlassFishとは独立して単体で使用することができます。
また、Grizzlyは独自の進化を遂げていっています。
現在、Grizzlyの最新バージョンは1.7.3.1です。

今回紹介するGrizzlyは少しバージョンが古いのですが、
GlassFish v2.x系にバンドルされているGrizzly 1.0.x(1.0.19)について
紹介します。

●Grizzly誕生の背景

まず、Grizzlyのできあがった背景について説明します。
Grizzlyは元々GlassFishのHTTPをハンドリングするHTTPサーバを
開発するプロジェクトとして開始しました。
それまでのSunのアプリケーションサーバは、TomcatのCoyoteを
内部で使用していたのですが、GlassFishではCoyoteを使用せずに
JavaのNew I/Oを使用して実現するHTTPサーバとして実験的に
作成を開始しました。
(Grizzly1.0.19でもHTMLの解析等で一部apacheのAPIを使用してます)

そして開発を進めて行くに従い、Grizzlyの潜在能力の高さが判明してき、
HTTPだけではなく、他のプロトコルも扱うことのできる汎用的な
ネットワークサーバエンジンになりえることが分かってきました。

その結果、現在ではHTTP以外にTCP,UDPといった下位のレイヤー
プロトコルを始めとし、TLS,FTPやSIP等といったマルチプロトコルに
対応するハイパフォーマンスなネットワークサーバエンジンになっています。

ですので、仮に開発者が新たに独自のネットワークサーバを構築する
必要がでてきた場合、GrizzlyのAPIを一部拡張して実装して頂くことで
ハイパフォーマンスな独自ネットワークサーバを構築することも可能になっています。

例えば、GrizzlyのHTTPサーバエンジン以外の使用例として
独自のsyslogサーバを構築することも可能になります。

Grizzlyの背景とできることは上で説明しましたが、何故今ここで
Grizzlyという新しいネットワークサーバエンジン(フレームワーク)が
登場したのでしょうか。以下ではWebサーバの実装における過去の歴史と
Grizzlyが登場した理由を説明します。

●Web サーバの実装における歴史

Webサーバは古くはCERN,NCSA等といった所で作成されていましたが、
Apacheでhttpdが作成された後はApacheにシェアを奪われていきました。
こういった古いWebサーバや初期のApache httpdはプロセス起動型のサーバ
の実装になっていました。



このプロセス起動型のサーバではHTTPクライアントであるブラウザからリクエストがくる度に
httpdのプロセスを起動しリクエスト数が多くなると子プロセスをfork()して
処理を行っていました。
当時のWebサーバは今程リクエスト数も多くなく、FastCGI等も無いこの時代には
サーバサイドで実行するプログラム(Perl,C等のCGI)もfork()して子プロセスで
実行していました。
しかし、プロセスのfork()はシステム(OS)に対して多大な負担を掛けます。
そこで登場したのがマルチスレッド型のサーバです。



マルチスレッド型のサーバでは単一のプロセス内でリクエスト毎にスレッドを起動し、
各スレッドでHTTPの処理を行うことで、子プロセスを起動するよりシステムに対する
負担は大幅に軽減するようになります。
この頃になり、マルチスレッドサーバ上でサーバサイドで実行する
プログラムとしてServletが脚光を浴びました。

しかし、実際のWebサーバの実装は上に説明した程簡単ではありません。
上記の図中にスレッドの起動プログラム例を記載していますが、
Webサーバのように大量のリクエストを扱うようなプログラムの
場合、accept()の処理は負荷はあまり掛からないのですが、
スレッドの起動はaccept()の処理に比べ非常に負荷が高くなって
しまいます。
この状況ではパフォーマンスにボトルネックが生じます。



そこで、スレッドの起動に関する問題を軽減する為に、実際のWebサーバ
ではaccept()処理とスレッドの処理を分離し、間に接続キューを
設けることでaccept()の処理を素早く受け流すことができるように
なります。(SunのWeb ServerはC++で上記のように実装)
そして、実際にHTTPの処理を行うワーカスレッドが接続キューから
コネクションを取得し、HTTPの処理を行うようになります。

他のサーバの実装を細かく見たことはありませんが、恐らく現在の
Webサーバの実装は似たような実装になっているのではないかと
思います。

●マルチスレッドサーバの問題 (Blocking I/O型サーバ)

ソケット通信を行うプログラムを実装する場合、accept()や
I/Oのread(),write()等を使用しますが、これらのメソッドは処理を
ブロックします。



例えば、ServerSocket#accept()メソッドはクライアントが接続するまで
待ち状態になり、クライアントからの接続があって初めてメソッドの処理を
終了します。
複数のクライアントからの接続を処理できるようにする為には、
サンプルのようにスレッドを生成し実際の処理は別スレッドで行うように
実装します。
このような実装の場合、接続してくるクライアントの数が増えれば増える程、
大量のスレッドが生成されることになります。
そして、スレッドが大量に生成される場合、大量のメモリが必要になってきます。

下記のグラフではJavaでスレッド数を増やしていった場合に実際に必要な
スレッドのスタックサイズを現していますが、これによると
10,000スレッドを同時に生成した場合、約10Gバイトのメモリが必要になっています。
また、2万スレッドになると約20Gバイトのメモリが必要になっていることがわかります。

このように、単純にスレッドを増やしていくモデルのサーバはアクセス数が増えれば増える程
大量のメモリを消費していくことがわかります。




●C10K問題を解決できるサーバ (Non Blocking I/O型サーバ)

GlassFish(Grizzly)は上記の問題を解決するためにNon Blocking I/Oを使用して
実装が施されています。

Non Blocking I/Oを使用するとスレッドの作成数を減らすことができます。



実際には、Java NIOではServerSocketChannel#accept()メソッドでaccept処理
を行いますが、この処理はブロックしません。
仮にこのメソッドが呼び出された時にクライアントからの接続がない場合は、
すぐにnullを返します。
ServerSocket#accept()メソッドのように処理をブロックしないので、
クライアントのリクエスト毎に新たなスレッドを生成する必要もなくなります。

言い換えると、ServerSocketChannel#accept()を呼び出した元のスレッドで
引き続き処理を続けることができるようになります。

※ つまり複数のリクエストを処理するために、接続毎に新規スレッドを
生成しなくてもよいようになります。


Grizzlyの開発者の1人であるJean-Francoisは次のように述べています。

「Grizzlyではたった30スレッドで10,000の接続を捌くことができます。」
This strategy prevent one thread per request, and enable Grizzly to server more that 10 000
concurrent users with only 30 threads.



●Asynchronus Request Processing(ARP)

さて、GrizzlyはJava New I/Oで実装されているだけでもすごいのですが
さらに、ARPも実装されています。

ARPはComeのアプリケーションや、ビジネスプロセスの処理にとても
時間が掛かるような場合、つまり長時間接続を保持しなければならない
アプリケーションの動作も実現できるように実装されています。

APRも又1つの接続辺り1スレッドを消費しないように実装されているため、
今までのマルチスレッドサーバ、Synchronus Request Processingに比べ
Comet等のアプリケーションを実行する環境としても最適です。

●まとめ

Grizzlyは汎用的なネットワークサーバエンジンです。
独自にサーバを構築する際はGrizzlyのフレームワークを再利用することで
ハイパフォーマンスな独自サーバを構築することが可能になります。

GrizzlyはJava NIOで実装されARPに対応しているのでComet等の
アプリケーションの実行環境としても最適です、そしてパフォーマンスも良いのです。
GlassFish(Grizzly)を使用して新しい先進的なアプリケーションを作成してください。



最後に、今回はGrizzlyの概要について紹介しました。
GlassFish(Grizzly)が何故パフォーマンスがよいのかの概要を掴んでいただけたのでは
ないかと思います。

次回は、さらに深くGrizzlyの内部の実装を知りたい方、もしくはJava NIOで実装された サーバに興味のある方を対象に、Grizzlyのソースコード(Grizzly 1.0.19)の見方を紹介します。

Permalink     No Comments
Track Back :


JJUG イベントの発表資料(Grizzly)


先日、開催されたJJUGのイベントで発表時に使用した資料を公開します。

恐らくJJUGでも同様の資料が公開されるかと思いますが、どちらから
リンクが貼られるかが分からないので念のため、自分のブログからも
取得できるようにしておきます。

本資料では、Grizzly1.0.x系の内部実装について詳しく紹介しています。
Grizzlyに興味のある方、Java NIOについて興味のある方は、
是非ご覧ください。

資料はこちらから

PS.
説明が無いと資料の内容を理解して頂くことは難しいとは思いますが、
説明は後日ということで。(^_^;)

Permalink     No Comments
Track Back :