我有一個控制器,我試圖從遠程源獲取XML文件。一次多個Nokogiri請求
喜歡的東西:
@artist = Nokogiri.XML(open(url).read)
不過,我想立刻獲得不同的數據來執行這些倍數。我能以某種方式使用線程嗎?
單獨執行需要400ms。所以當它們連續執行三次時,響應高達大約1s +。
我有一個控制器,我試圖從遠程源獲取XML文件。一次多個Nokogiri請求
喜歡的東西:
@artist = Nokogiri.XML(open(url).read)
不過,我想立刻獲得不同的數據來執行這些倍數。我能以某種方式使用線程嗎?
單獨執行需要400ms。所以當它們連續執行三次時,響應高達大約1s +。
是的,你可以使用線程:
named_urls = {
artist: 'http://foo.com/bar',
song: 'http://foo.com/jim',
# etc.
}
@named_xmls = {}
one_at_a_time = Mutex.new
named_urls.map do |name,url|
Thread.new do
doc = Nokogiri.XML(open(url).read)
one_at_a_time.synchronize{ @named_xmls[name] = doc }
end
end.each(&:join)
# At this point @named_xmls will be populated will all Nokogiri documents
我不能肯定,如果在一個共享的哈希寫入不同的密鑰,需要一個互斥或沒有,但它不會傷害是安全的。
太棒了。這工作很好!非常感謝。 – Jonovono
對於大量的網址,您無法打開大量的線程,因爲您將飽和連接帶寬,並且您將開始發生連接錯誤。對於我的特定電纜調制解調器和特定的服務器,我發現16個線程是一個很好的價值。
我使用了一個Mathematica來控制和改變我的ruby web scrapping程序的線程數並監視它的性能以獲得不同數量的線程。這是結果:
,而不是直接使用Thread.new
,我寫道,打開一個新的線程,只有當線程總數少於你的配置最大的包裝功能:
def maybe_new_thread
File.open('max_threads.cfg', 'r') { |file| @MAX_THREADS = file.gets.to_i }
if Thread.list.size < @MAX_THREADS
Thread.new { yield }
else
yield
end
end
請注意,所需線程的最大數量僅爲存儲在名爲max_threads.cfg
的文件中的數字,並且每次調用函數時都會讀取此文件。這允許您在程序運行時更改此變量的值。
程序的一般結構是這樣的:
named_urls = [ 'http://foo.com/bar', (... hundreds of urls ...),'http://foo.com/jim']
named_urls.each do |url|
maybe_new_thread do
doc = Nokogiri.HTML(open(url))
process_and_insert_in_database(doc)
end
end
注意,每個線程存儲其結果在數據庫中,所以我並不需要使用Mutex類來協調線程之間的任何東西。
當我插入到數據庫中時,我將包含每個結果插入時的精確時間的列。這是至關重要的,以便您可以計算您獲得的表現。確保你用毫秒支持來定義這個列(我使用MariaDB 5.3)。
這是我在用數學控制線程的最大數量,並實時繪製圖中的代碼:當它運行
named_urls = {
'http://foo.com/bar', (... hundreds of urls ...),'http://foo.com/jim',
}
named_urls.each do |url|
maybe_new_thread do
doc = Nokogiri.HTML(open(url))
process_and_insert_in_database(doc)
end
end
setNumberOfThreads[n_] := Module[{},
Put[n, "max_threads.cfg"];
SQLExecute[conn,"DELETE FROM results"]]
operationsPerSecond := SQLExecute[conn,
"SELECT
(SELECT COUNT(*) FROM results)/
(SELECT TIME_TO_SEC(TIMEDIFF((SELECT fin FROM results ORDER BY finishTime DESC LIMIT 1),
(SELECT fin FROM results ORDER BY finishTime LIMIT 1))))"][[1, 1]];
cops = {};
RunScheduledTask[AppendTo[cops, operationsPerSecond], 2];
Dynamic[ListLinePlot[cops]]
,一旦你看到的是,性能穩定,您可以使用setNumberOfThreads[]
更改線程數,並查看性能的影響。
最後一個評論。而不是直接使用開放式的URI的開法,我用這個包裝,所以這是錯誤的情況下,它會自動重試:
def reliable_open(uri)
max_retry = 10
try_counter = 1
while try_counter < max_retry
begin
result = open(uri)
return result
rescue
puts "Error when trying to open #{uri}"
try_counter += 1
sleep try_counter * 10
end
end
raise "Imposible to open after #{max_retry} retries"
end
你可能想看看在百頭巨怪和水潤,如果你擔心並行加載網址。他們是經過充分測試的工具,而不是自己寫。 –