#概要
どうもです。
ノンブロッキングI/Oの概念やjavaでの実装を調べてる中で、
javaのクラス間の関係を図とかで一気に登場人物が欲しかったので作りました。
また、
「同期I/Oや非同期I/Oに対するメリット」
「一番の(構造上の)違い」
という感じの図も欲しくなり作りました。
というわけで行ってみましょう。
#内容
##ノンブロッキングI/Oとは
###~背景~ C10K問題
主にWebシステムにおいて、
「サーバに対するクライアントの数が増えるとやばいよね」
という問題がことの発端です。
C10K問題(英語: C10K problem)とは、Apache HTTP ServerなどのWebサーバソフトウェアとクライアントの通信において、クライアントが約1万台に達すると、Webサーバーのハードウェア性能に余裕があるにも関わらず、レスポンス性能が大きく下がる問題である。
from wiki...
###ノンブロッキングI/Oでやること
通信方式の一つです。
非同期I/O同様サーバプロセスが複数の接続を保持することができる方式ですが、
若干やることが違います。
正確性を犠牲にして噛み砕いた表現をすると、
非同期I/O
-> 接続ごとにスレッドを用意する
ノンブロッキングI/O
-> 複数の接続を一つのスレッドで相手取る
といったところです。
「接続を管理台帳みたいなところに登録しておいて、
定期的にそれらの接続に何か届いていないか、とかを確認して届いていれば処理を行う」
というものです。
###ノンブロッキングI/Oの考え方と従来方式の違い
同期I/Oや非同期I/Oの違いを調べた感じは以下のような感じです。
##JavaでのノンブロッキングI/O通信の実装に関わるクラス
###登場人物と事前準備
JavaでノンブロッキングI/O通信を実装する場合、java.nioパッケージの中身をいくつか使うと思います。
割とよく見る使い方はこんな感じです↓。こういう感じで事前準備をします。
###そのあと「セレクタ」でやること
ノンブロッキングでは「セレクタ」の処理が特色というか、大事なところです。
SocketChannelを登録するところですが、関連する処理は↓のような感じです。
-
1.新しい接続が来ると、先に登録したサーバーソケットチャネルがacceptメソッドで取り出します。
-
2.その接続(SocketChannel)をキーとして「セレクタ」の「キーセット」に登録します。
その時に、「その接続に対してやりたいこと(リクエストの中身を読み出すとか)」を設定します。
(interestOpsとか、registerといったメソッドです) -
3.その後、select()というメソッドを読ぶと
「やりたいことができる状態になっているか?」
がOSに問い合わせられ、できる状態であれば、「選択されたキーセット」に入ります。 -
4.選択されたキーセットに入ってしまえば、あとはそこから接続をイテレータで取り出して処理するだけです。
##SelectorとSelectionKey
Selectorとその中で使われるSelectionKeyはいくつかのフィールドを持ちます。
- 最初に登録される「キーセット」
- 読み出しや書き込みなどの操作ができる状態でselect()を使うとキーセットからこちらに登録される「選択されたキーセット」
- 接続が切断された時等に追加される「取り消されたキーセット」
という3つのセットがあります。選択されたキーセットには、
直接キーを登録するようなAPIはありません。
また、実際に登録されるキーであるオブジェクトのSelectionKeyには、
- 操作種別を指定できる「対象セット」
- 実際に対象セットに指定した操作が可能かという情報が登録される「実行可能操作セット」
があります。実行可能操作セットには、直接値を設定するようなAPIはありません
これらが前述の処理の中でどう動くかというと、↓のような感じになります。
#おわりに
調べてみて、
Selectorのselect()や
SelectionKeyの振る舞い、関連性が分かりにくいな、
と思っていたので整理してある程度頭がスッキリした感じはします。
あと、正直ServerSocketとSocketの具体的な役割の違いも怪しかったので
いい勉強になりました。
間違い、ご指摘等あればいただけるとありがたいです。
以上になります。
ご一読ありがとうございました。