3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Kotlinでnioを使ってhttp server

Posted at

JJUG CCC 2017 Fallで「新しいプログラミング言語の学び方」のセッションでhttp serverを作っていました。自分でhttp serverを書いたのは遥か昔のことです。セッションを聞いてみて、久しぶりに自分でも書いてみるか、という気になり、勢いで書いてみました。あまり、ちゃんとしてはいないですが…。

生のソケットを扱うことは普通のWebアプリケーションの開発者にとってはあまりないことなので、少し新鮮でした。

http serverのコードはこちら。なるべく、発表で使われたソースコードの構造に合わせています。

で、自分用のメモです。

前提

まず、普通にブロッキングIOでソケットを扱うと、JavaやScalaと違いがないので、nioを使ってなるべくノンブロッキングで処理します。nioでサーバーソケットを扱うのはこれが初めてです。

nioに関する処理はSimpleHttpServer.ktにあります。

大まかな構造

メインスレッドの中でselectします。acceptすると読み込みを開始します。

読み込みは、readableになると別のスレッドで読み込みをして、メインスレッドはブロックしません。

HTTPヘッダーの読み込みが完了すると、メインスレッドのselectでwritableになったら、別スレッドに通知します。ここはread用のスレッドと同じようなパターン。ただ、writeの時に、handlerを呼び出して、書き込みのデータを準備します。ここがブロックされるとボトルネックになるので、ここでさらに別スレッドを立ち上げてhandlerを呼び出して書き込みまで行います。

ここまでが大きな流れです。

初期化

nioでサーバソケットを扱うときは、グローバルにはSelectorとchannelを利用します(L15, L16)。
そして、チャネルをノンブロッキングに指定して(L19)、ソケットを初期化します(L22)。最後にチャネルでacceptのイベントを登録します(L23)。

メインスレッド

runメソッド(L31)の中の無限ループでselectします(L37)。selectしてイベントがある場合は、イベントの種類に応じて処理して(L39-L48)、最後にキーを削除しています(L49)。ここではあとでまとめて削除していますが、イテレータでループしている最中にremoveしてもいいかも。

扱うイベントの種類は、accept/read/writeです。acceptのときは(L72)、ServerSocketChannelでacceptして、ソケットを取得します(L75)。ここでも、ノンブロッキングに設定して(L77)、読み込み可能な状態を待つように登録します(L81)。
各ソケットに対応したデータを紐づけて登録できるので、分断された処理も継続できます。

readableになったら、read用のキューにselectedKeyを登録しています。SynchronizedQueueなので、データが登録されたら別スレッドで処理が開始されます(L84-L86)。

writableになった場合も、メインスレッドの処理は同じです(L87-L90)。

ReadWorker

ReadWorkerでソケットからデータを読み込みます(L127)。一度の処理ですべてのヘッダーを読み込めるとは限らないので、ここでは、\r\n\r\nが現れるまで読み込みが継続します。

読み込みが完了したら、読み込み終了の状態にしてwriteのイベントを登録します。

WriteWorker

WriteWorkerでは、writableになったらExecutorでスレッドを生成して、実行します(L162)。handlerが処理した後(L173)、channelに書き込んで(L176)、チャネルを閉じています(L177)。ここのtry - catchは余分でしたね。

パフォーマンスはどう?

発表内容でのブロッキングIOでの処理とnioのノンブロッキングの処理と比べて、パフォーマンスは有意な差は見られません。(てへぺろ)

ただ、それは接続数が少ない場合で、接続数がおおくなると、ノンブロッキングの方が処理を継続しやすいです。(ブロッキングする方だとエラーが発生)。でも、おそらく、OSの設定を変更すれば大丈夫なはず。
websocketとかhttp2とか永続的なコネクションを扱うようになると、ブロッキングIOでスレッドでとやると、ある程度のオーバーヘッドが発生したりしてしまいそう。また、メモリも貧乏人には勿体無いかも。

おわり

とりあえず、ノンブロッキングはめんどくさいので、そういうことは若者にやってもらいましょう。
まじめに作ると結構大変そう。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?