目次
- Working with TCP Sockets 読書メモ 第0章 はじめに
- Working with TCP Sockets 読書メモ 第1章 はじめてのソケット
- Working with TCP Sockets 読書メモ 第2章 サーバのライフサイクル
- Working with TCP Sockets 読書メモ 第3章 クライアントのライフサイクル
- Working with TCP Sockets 読書メモ 第4章 データのやり取り
- Working with TCP Sockets 読書メモ 第5章 データの書き込みとバッファリング
- Working with TCP Sockets 読書メモ 第6章 はじめてのクライアント/サーバ
- Working with TCP Sockets 読書メモ 第7章 ノンブロッキングIO
- Working with TCP Sockets 読書メモ 第8章 コネクションの多重化(本記事)
コネクションの多重化
- コネクションの多重化とは、同時に複数のソケットを使用すること
select(2)
# ※擬似コード
# コネクションが配列で与えられるとする
connections = [<TCPSocket>, <TCPSocket>, <TCPSocket>]
loop do
# select(2) を使ってコネクションが読み込み可能か調べる
ready = IO.select(connections)
# 利用可能なコネクションからのみデータを読み取る
readable_connections = ready[0]
readable_connections.each do |conn|
data = con.readpartial(4096)
process(data)
end
end
IO.select
を使うことで、複数のコネクションを扱う際のオーバーヘッドを削減できる- 内部ではselect(2)が使われている
IO.select
は最大で4つ引数を取る( リファレンス )
for_reading = [<TCPSocket>, <TCPSocket>, <TCPSocket>]
for_writing = [<TCPSocket>, <TCPSocket>, <TCPSocket>]
IO.select(for_reading, for_writing, for_reading)
- 第1引数は読み込みを行いたい
IO
オブジェクトの配列 - 第2引数は書き込みを行いたい
IO
オブジェクトの配列 -
第3引数は例外待ちを行いたい
IO
オブジェクトの配列 -
IO.select
は2次元配列を返す - 第1要素は、第1引数で渡した
IO
オブジェクトのうち、ブロックせずに読み込み可能なものの配列 - 第2要素は、第2引数で渡した
IO
オブジェクトのうち、ブロックせずに書き込み可能なものの配列 -
第3要素は、第3引数で渡した
IO
オブジェクトのうち、例外待ち可能なものの配列 -
IO.select
は、処理可能なIO
オブジェクトがないとブロックする - 第4引数にタイムアウトの秒数を渡すと、指定した時間だけ待つ
- タイムアウトの秒数が経過したら
nil
が返る
- タイムアウトの秒数が経過したら
読み書き以外のイベント
IO.select
で発生するイベントは他にも以下のようなものがある
EOF
- 読み込み待ちの最中にEOFを受け取ると、
EOFError
が発生するか、nil
が返る
Accept
- 読み込み待ち中にコネクションが来ると、読み込み可能なソケットの配列の中に含まれて返却される
Connect
connect_nonblock
のErrno::EINPROGRESS
の際に、バックグラウンドの接続が完了したか確認できる
require 'socket'
socket = Socket.new(:INET, :STREAM)
remote_addr = Socket.pack_sockaddr_in(80, 'google.com')
begin
socket.connect_nonblock(remote_addr)
rescue Errno::EINPROGRESS
IO.select(nil, [socket])
begin
socket.connect_nonblock(remote_addr)
rescue Errno::EISCONN
# 接続成功
rescue Errno::ECONNREFUSED
# リモートホストから接続拒否
end
end
ハイパフォーマンスな多重化
- Rubyでは多重化のための機能は
IO.select
しか提供されていない - モダンなOSでは様々な多重化方法を提供しているが、select(2)は最も古くパフォーマンスに劣る
- select(2)は監視対象のコネクション数が増えると線形的にパフォーマンスが低下する
- poll(2)はselect(2)の代替だがそれほどの差はない
- (Linux)epoll(2) または (BSD)kqueue(2) は、select(2)およびpoll(2)のモダンな代替である
- EventMachineのようなハイパフォーマンスなネットワークライブラリはepoll(2)やkqueue(2)を使っている
- nio4r等のgemを使うことで、Rubyでもepoll(2)等を使えるようになる