はじめに
WebサービスのAPIをコールするような,
ローカルマシンのみで完結する処理と比べると,
この無駄な時間の間に他の処理ができるならば,
今回はReactorパターンという,
複数のwebサーバからHTML文章を取得してみる
同期処理
ひとまず非同期処理を忘れて,
非同期処理を採用する理由は,
リスト1 ひとつずつ同期的に取得
hosts.each do |host|
sock = TCPSocket.new(host, 80)
sock.write(request)
sock.read
sock.close
end
しかし,
マルチスレッド処理
次に処理時間を短縮するため,
本当にすべての処理が並列に動くとしたら,
リスト2 スレッドを使用
threads = hosts.map do |host|
Thread.new(host) do |h|
sock = TCPSocket.new(h, 80)
sock.write(request)
sock.read
sock.close
end
end
threads.each{|t| t.join}
Reactorパターンを使う
writeやreadのメソッドでの無駄な時間を減らすためにはどうしたらよいでしょう。
一番無駄なのが,
一番最初に書き込み可能になったソケット,
リスト3 Reactorパターンで非同期処理を行う
# ①
write_socks = hosts.map do |host|
TCPSocket.new(host, 80)
end
read_socks = []
# ②
write_proc = lambda{|sock|
sock.write(request)
}
# ③
read_proc = lambda{|sock|
sock.read
sock.close
}
# ④
until (write_socks + read_socks).empty?
# ⑤
r_socks, w_socks, e_socks = IO.select(read_socks, write_socks)
# ⑥
if ws = w_socks.first
write_proc.call(ws)
read_socks << ws
write_socks.delete(ws)
end
# ⑦
if rs = r_socks.first
read_proc.call(rs)
read_socks.delete(rs)
end
end
他のアプローチと比べると,
①の部分では,
②,
④のブロックがイベントループにあたります。書き込みソケット群,
⑤では,
⑥では書き込み可能状態となったソケットに対して処理を行います。②で定義したコールバック処理を実行し,
⑦では,