Recent Posts

RSS Feeds

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 :




Post a Comment:
Comments are closed for this entry.