次のようなユースケースで、HTTPリクエストを並列発行したいことがあるでしょう。
- URLのコンテンツ内容の監視(Webレスポンス監視)
- コンテンツの応答監視(ページの更新チェック)
- Webページのクロール(スクレイピング)
非同期処理と言ったらコールバックですね。ただ、コールバック地獄は避けたい。
そんな時には次のようにEventMachineとFiberを用いると、読みやすい非同期処理の実装が出来ます。
URLの数だけ並列実行する方法
とにかく速いですが、URLリストが増えた場合には次の問題が起きうるので注意が必要です。
- ファイルディスクリプタの上限を叩く可能性
- 接続先Webサーバの最大同時接続数を使い果たす可能性
- NATルータ機器を通してインターネットに出ている場合には、そのNATテーブルが溢れる可能性
- スパイク的なリクエストを発生させることで、下りネットワーク帯域を埋め尽くす可能性
require 'em-synchrony'
require 'em-synchrony/em-http'
urls = %w[
http://localhost:3000/?page=1
http://localhost:3000/?page=2
http://localhost:3000/?page=3
http://localhost:3000/?page=4
http://localhost:3000/?page=5
http://localhost:3000/?page=6
]
# カウンターを初期化
pending = urls.size
EM.synchrony do
urls.each do |url|
# 軽量スレッドであるFiberを呼び出す
Fiber.new do
# HTTPリクエストを発行する
http = EM::HttpRequest.new(url).post :body => request_body
# それぞれのURL取得後に実行する処理
# これらはそれぞれのリクエストが入り交じること無く実行されます
puts url
puts http.response_header # HTPレスポンスヘッダ
puts http.response_header.status # HTPレスポンスコードを出力
puts http.response # HTPレスポンスボディ
# カウンターをデクリメントする
pending -= 1
# 最後の実行時にEventMachineを止める
EM.stop if pending == 0
end.resume
end
end
# 全ての処理が終わったらこちらの処理が走ります
puts "All requests has finished."
最大並列実行数を指定してHTTPリクエストを発行する方法
サーバやネットワークに優しく通信をするコードは次の通りです。
require 'em-synchrony'
require 'em-synchrony/em-http'
require 'em-synchrony/fiber_iterator'
urls = %w[
http://localhost:3000/?page=1
http://localhost:3000/?page=2
http://localhost:3000/?page=3
http://localhost:3000/?page=4
http://localhost:3000/?page=5
http://localhost:3000/?page=6
]
EM.synchrony do
# 並列実行数
concurrency = 2
# FiberIteratorを用いて非同期処理らしさを隠す
EM::Synchrony::FiberIterator.new(urls, concurrency).each do |url|
# HTTPリクエストを発行する
http = EM::HttpRequest.new(url).get
## もしここでPOST通信する際には、内容を:bodyに渡します
## http = EM::HttpRequest.new(url).post :body => { name: 'hoge' }
# それぞれのURL取得後に実行する処理
# これらはそれぞれのリクエストが入り交じること無く実行されます
puts url
puts http.response_header # HTPレスポンスヘッダ
puts http.response_header.status # HTPレスポンスコードを出力
puts http.response # HTPレスポンスボディ
end
# 全ての処理が終わったらこちらの処理が走ります
puts "All requests has finished."
# EventMachineを止めます
EM.stop
end
不備等ございましたら是非、編集リクエストなどからお知らせ頂けると幸いです。
参考サイト
- Em synchrony について
http://www.slideshare.net/cuzic/em-synchrony