2/28まで皆様、Grizzlyのロゴコンテストが発表されました。期日は、2/28までです。皆様かわいらしいキャラクターロゴを作成してください。Grizzlyのメールアドレスまで作成したロゴを送信してください。英語が苦手という方がいらっしゃいましたら私のメールアドレスまでお送り頂いても結構です。Grizzly:jfarcandあっとapache.orgYoshio.TeradaあっとSun.COMねこびーんの作者の、かねうちさん等かわいらしい熊さんを書いてくれないかなぁ。今日は、Grizzlyについての情報のお知らせです。GrizzlyはGlassFishのHTTP等のハンドリングを行うサーバプログラムとして使用されています。2.0に関する情報を頂きました。1.x系よりもかなり分かり易くなっていると書かれています。2.0では汎用のサーバエンジンとして扱い易くなりそうです。実際、プレゼンの中では独自のログサーバを作成するサンプルコードが記載されていました。剛さんがGrizzlyでCometに関する記事を書いてくださっています。下記の記事を参考にされては如何でしょうか?サンプルコードもあります。1.8.0がリリースされました。1.8.0は多くのバグを修正し、またパフォーマンスも改善されております。組み込みでOSGiに対応しています。I/Oについて簡単に復習しておきたいと思います。NIOについて説明されている記事を読んで理解してください。I/Oを学ぶことができる参考URLを示しています。I/Oを使用して実装されたサーバより新規スレッドを作成する必要がなくスレッド数を少なくすることができると説明しましたが、では具体的にどのようにして実装されているかを説明致します。I/OのAPIを紹介します。個別の入出力操作を実行できる接続を表します。Blockingにできるチャネルは、java.nio.channels.SelectableChannelクラスを継承したサブクラスで、java.nio.channels.SocketChannelやjava.nio.channels.ServerSocketChannelクラスがあります。Blockingモードに調整することができます。ServerSocketChannelを使ってチャネルをノンブロッキングモードにする為には下記のようにserverChannel.configureBlocking(false)を記載します。対してregister(登録)します。I/Oの中でとても重要な箇所です。I/Oではjava.nio.channels.ServerSocketChannel.accept()メソッドでアクセプト処理を行います。保留されている接続がない場合、直ちにnullを返します。ServerSocket.accept()のように新たなクライアントからの接続がくるまで待ち続けることはありません。クライアントからの接続を待たないということはサーバ側ではどのタイミングでアクセプト処理を行えばよいのかが分からなくなります。そこで、利用できるチャネル(接続)を取得する為にセレクタ(Selector.select())を使用します。チャネルをセレクタに登録しておき、セレクタの中で利用できるようになったチャネルをSelectionKeyとして取得して入出力操作を行うことができます。複数の入出力操作を同時に行うことができるようになります。NIOを使用して作成してみましたが、下記ではクライアントからの接続毎にスレッドを生成していないことが御分かりいただけるかと思います。マルチスレッド型のサーバのようにコネクション毎に新規スレッドを生成していないことがわかります。I/Oを使用して作成されたサーバは、接続毎に新規スレッドを作成しなくてもよくなるため、既存のサーバ実装とは特に大量のアクセス等が発生した際に大きな差がでてきます。メモリの消費量も少なくて済むので高負荷時になればなるほど使用するサーバリソースが大きく変わってくるかと思います。1.0.19の中でどのようなクラスが重要なのか等ソースコードを見るために必要な情報を紹介したいと思います。Grizzlyのソースコードを読む前に、上記のSelector,SocketChannelServerSocketChannel,SelectionKey等のクラスを理解しておいてください。C10K問題に対応するGlassFish(Grizzly)さて、おまたせ致しました。本日より、JJUGで発表したGrizzlyについて紹介したいと思います。I/Oを使って記述された汎用的なネットワークサーバエンジンです。GlassFishとは独立して単体で使用することができます。Grizzlyは独自の進化を遂げていっています。現在、Grizzlyの最新バージョンは1.7.3.1です。1.0.x(1.0.19)について紹介します。●Grizzly誕生の背景まず、Grizzlyのできあがった背景について説明します。Grizzlyは元々GlassFishのHTTPをハンドリングするHTTPサーバを開発するプロジェクトとして開始しました。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サーバは古くはCERN,NCSA等といった所で作成されていましたが、Apacheでhttpdが作成された後はApacheにシェアを奪われていきました。httpdはプロセス起動型のサーバの実装になっていました。このプロセス起動型のサーバではHTTPクライアントであるブラウザからリクエストがくる度にhttpdのプロセスを起動しリクエスト数が多くなると子プロセスをfork()して処理を行っていました。当時のWebサーバは今程リクエスト数も多くなく、FastCGI等も無いこの時代にはサーバサイドで実行するプログラム(Perl,C等のCGI)もfork()して子プロセスで実行していました。プロセスのfork()はシステム(OS)に対して多大な負担を掛けます。そこで登場したのがマルチスレッド型のサーバです。マルチスレッド型のサーバでは単一のプロセス内でリクエスト毎にスレッドを起動し、各スレッドでHTTPの処理を行うことで、子プロセスを起動するよりシステムに対する負担は大幅に軽減するようになります。この頃になり、マルチスレッドサーバ上でサーバサイドで実行するプログラムとしてServletが脚光を浴びました。実際のWebサーバの実装は上に説明した程簡単ではありません。上記の図中にスレッドの起動プログラム例を記載していますが、Webサーバのように大量のリクエストを扱うようなプログラムの場合、accept()の処理は負荷はあまり掛からないのですが、スレッドの起動はaccept()の処理に比べ非常に負荷が高くなってしまいます。この状況ではパフォーマンスにボトルネックが生じます。そこで、スレッドの起動に関する問題を軽減する為に、実際のWebサーバではaccept()処理とスレッドの処理を分離し、間に接続キューを設けることでaccept()の処理を素早く受け流すことができるようになります。ServerはC++で上記のように実装)そして、実際にHTTPの処理を行うワーカスレッドが接続キューからコネクションを取得し、HTTPの処理を行うようになります。他のサーバの実装を細かく見たことはありませんが、恐らく現在のWebサーバの実装は似たような実装になっているのではないかと思います。I/O型サーバ)ソケット通信を行うプログラムを実装する場合、accept()やI/Oのread(),write()等を使用しますが、これらのメソッドは処理をブロックします。ServerSocket#accept()メソッドはクライアントが接続するまで待ち状態になり、クライアントからの接続があって初めてメソッドの処理を終了します。複数のクライアントからの接続を処理できるようにする為には、サンプルのようにスレッドを生成し実際の処理は別スレッドで行うように実装します。このような実装の場合、接続してくるクライアントの数が増えれば増える程、大量のスレッドが生成されることになります。スレッドが大量に生成される場合、大量のメモリが必要になってきます。下記のグラフではJavaでスレッド数を増やしていった場合に実際に必要なスレッドのスタックサイズを現していますが、これによると10,000スレッドを同時に生成した場合、約10Gバイトのメモリが必要になっています。2万スレッドになると約20Gバイトのメモリが必要になっていることがわかります。このように、単純にスレッドを増やしていくモデルのサーバはアクセス数が増えれば増える程大量のメモリを消費していくことがわかります。I/Oを使用して実装が施されています。I/Oを使用するとスレッドの作成数を減らすことができます。NIOではServerSocketChannel#accept()メソッドでaccept処理を行いますが、この処理はブロックしません。仮にこのメソッドが呼び出された時にクライアントからの接続がない場合は、すぐにnullを返します。ServerSocket#accept()メソッドのように処理をブロックしないので、クライアントのリクエスト毎に新たなスレッドを生成する必要もなくなります。言い換えると、ServerSocketChannel#accept()を呼び出した元のスレッドで引き続き処理を続けることができるようになります。複数のリクエストを処理するために、接続毎に新規スレッドを生成しなくてもよいようになります。Grizzlyの開発者の1人であるJean-Francoisは次のように述べています。「Grizzlyではたった30スレッドで10,000の接続を捌くことができます。I/Oで実装されているだけでもすごいのですがさらに、ARPも実装されています。ARPはComeのアプリケーションや、ビジネスプロセスの処理にとても時間が掛かるような場合、つまり長時間接続を保持しなければならないアプリケーションの動作も実現できるように実装されています。Processingに比べComet等のアプリケーションを実行する環境としても最適です。●まとめGrizzlyは汎用的なネットワークサーバエンジンです。独自にサーバを構築する際はGrizzlyのフレームワークを再利用することでハイパフォーマンスな独自サーバを構築することが可能になります。NIOで実装されARPに対応しているのでComet等のアプリケーションの実行環境としても最適です、そしてパフォーマンスも良いのです。GlassFish(Grizzly)を使用して新しい先進的なアプリケーションを作成してください。最後に、今回はGrizzlyの概要について紹介しました。GlassFish(Grizzly)が何故パフォーマンスがよいのかの概要を掴んでいただけたのではないかと思います。1.0.19)の見方を紹介します。イベントの発表資料(Grizzly)先日、開催されたJJUGのイベントで発表時に使用した資料を公開します。JJUGでも同様の資料が公開されるかと思いますが、どちらからリンクが貼られるかが分からないので念のため、自分のブログからも取得できるようにしておきます。本資料では、Grizzly1.0.x系の内部実装について詳しく紹介しています。説明は後日ということで。使用に同意したとみなされます。