Fiberとnio4rでサーバー
Fiberとnio4rを組み合わせてサーバーを作りました。あたまバグりそうになるのでよかったら見てってください。
Fiberとnio4rを組み合わせる
Fiberは、軽量なスレッドを作成できます。このスレッド上で、途中まで実行した処理を中断し、再開することができます。 この特徴をうまく使ってやると、nio4rのselectと組み合わせて効率的な通信処理を実現できます。
非常にややこしいので、注意してください。
クライアントとの通信
Fiber.newで、ブロックにとった処理を行うFiberを作成できます。作成した時点では処理は実行されません。Fiber#resumeを呼ぶと、Fiber.yieldまで処理を実行したあと、呼び出し元に戻ります。この時点で、Fiberは処理が「中断」された状態になります。このあと、再びFiber#resumeを呼ぶと、中断されたところから処理を再開します。
f = Fiber.new do puts 'Hello1' Fiber.yield puts 'Hello2' end f.resume # => Hello 1 f.resume # => Hello 2
ここで、nio4rのselectと組み合わせて効率的な通信を行うことを考えます。selectは複数のIOを待って、利用可能なものを選択できます。ブロッキングが起こりそうなときに処理を中断してselectし、またなくても良くなったら処理を再開するようにFiberを制御すると、Fiberはノンブロッキングに処理を実行できます。
コードにするとこういう感じです。
ioは、ノンブロッキング通信が可能な状態で渡されるソケットです。@selector
はNIO::Selector
です。
Fiber.new do |io| message = io.read_nonblock 5000 @selector.register io, :w Fiber.yield io.write_nonblock "response" io.close end
ノンブロッキングでreadしたら、次は書き込みを行いたので、書き込みを待つようにselectorに登録します。こうなったらいったん処理を抜けます。 処理が再開されたら、それはすなわち書き込みができる状態ということなので、ソケットに書き込みます。
処理の振り分け
振り分ける側は、適切な(=ノンブロッキングで処理が可能な)fiberをresumeできるように、@fibers
にioとfiberの組み合わせを持つようにします。
selectで適切なioがわかるので、ioをキーに、そのioを処理するfiberを値にもつハッシュを作ってやれば、どのfiberをresumeすべきかは簡単にわかります。
@srv = TCPServer.new 13_000 @selector.register @srv, :r loop do @selector.select do |m| case m.io when @srv socket = m.io.accept_nonblock @fibers[socket] = communication_fiber @selector.register socket, :r else f = @fibers[m.io] @selector.deregister m.io f.resume m.io end end end
これが非常にややこしいんだなぁ…
Fiberなサーバー
ソースコード全量がこちら。前回に引き続き、最低限のHTTPレスポンスを返すサーバーを作ってます
gistb6da8b93f525ccfdb150f160717fa786
ブラウザで表示したようす。受け取ったメッセージをそのまま返すので、WebブラウザのHTTPリクエストが書いてあります。