net/http で Keep-Alive なやりとり
Ruby でよく使うライブラリ net/http なんですが,コネクション張り続けて通信するにはどうしたらいいんだろう.という話.
いろいろ弄った結果
ポイントは二つくらい
念のため Net::HTTP:Get のインスタンスに以下のようなヘッダエンティティtをくっつける.Net::HTTP::Get#['Connection'] = 'Keep-Alive'
Net::HTTP.start や,Net::HTTP#start を使ってコネクションを張る
2回目以降の要求をする時において,前回の要求から時間が空いていると,サーバがコネクションを断ち切ってしまうので,コネクションの張り直し手続きが必要になる.
また3回目の要求の際は,意図的に間隔を空けて,サーバからコネクションを断ち切られてしまった場合を再現している.
#!/usr/bin/ruby -Ku require 'net/http' require 'uri' host = 'd.hatena.ne.jp' port = 80 path = '/' sleep_time = 7 # 要求・応答ヘッダ出力 def print_key_and_val(hash) puts "-- #{hash.class} --" hash.each do|k,v| puts "#{k} => \t#{v}" end puts '' end http = Net::HTTP.new(host,port) req = Net::HTTP::Get.new(path) # ポイント1 req['Connection'] = 'Keep-Alive' # コネクションを張る(ポイント2 http.start # 1回目の要求 puts '## First' print_key_and_val req print_key_and_val http.request(req) # 2回目の要求 puts '## Second' print_key_and_val req print_key_and_val http.request(req) # 間隔を空ける puts "\n## sleep #{sleep_time}s\n\n" sleep sleep_time # この間に,d.hatena サーバからコネクションを切られてしまう # 3回目の要求(ポイント3 # sleep 中に,サーバからコネクションを切られているので,うまくいかない. # EOFError が発生してしまう.この例外を捕捉し,コネクションを張りなおす # 事で対処をした. puts '## Third' retry_flag = true # 張りなおしてもダメだった時を判別するためのフラグ begin print_key_and_val req print_key_and_val http.request(req) rescue EOFError => e puts '********************' puts '** Catch EOFError **' puts '********************' if retry_flag http.finish # 一度こちらからも切って, http.start # コネクション張り張り直し retry_flag = false retry else raise EOFError, e.message end end # コネクションを切る http.finish
実行結果
$ ruby http_timeout.rb ## First -- Net::HTTP::Get -- connection => Keep-Alive accept => */* -- Net::HTTPOK -- vary => Accept-Encoding connection => Keep-Alive content-type => text/html; charset=euc-jp date => Fri, 21 Mar 2008 09:06:09 GMT server => Apache keep-alive => timeout=7, max=5 transfer-encoding => chunked ## Second -- Net::HTTP::Get -- connection => Keep-Alive accept => */* host => d.hatena.ne.jp -- Net::HTTPOK -- vary => Accept-Encoding connection => Keep-Alive content-type => text/html; charset=euc-jp date => Fri, 21 Mar 2008 09:06:09 GMT server => Apache keep-alive => timeout=7, max=4 transfer-encoding => chunked ## sleep 7s ## Third -- Net::HTTP::Get -- connection => Keep-Alive accept => */* host => d.hatena.ne.jp ******************** ** Catch EOFError ** ******************** -- Net::HTTP::Get -- connection => Keep-Alive accept => */* host => d.hatena.ne.jp -- Net::HTTPOK -- vary => Accept-Encoding connection => Keep-Alive content-type => text/html; charset=euc-jp date => Fri, 21 Mar 2008 09:06:17 GMT server => Apache keep-alive => timeout=7, max=5 transfer-encoding => chunked $
その他
実際,書く時は,イチイチ begin ... rescue ... end で EOFError を補足するよりも,def で EOFError 捕捉機能つきの要求メソッドを用意するか,下記のようにしてしまった方が,賢く見えるかも.
class Net::HTTP alias_method :old_request, :request def request(req, body = nil, &block) retry_flag = true begin old_request(req, body, &block) rescue EOFError => e if retry_flag retry_flag = false finish start retry else raise EOFError, e.message end end end end
こうすれば,再接続を意識せずにコードが書ける.本当は
レスポンスヘッダ Keep-Alive の max値も考慮に入れなければならないんでしょうね...