make loop

This commit is contained in:
chai2010
2015-12-26 20:05:30 +08:00
parent 82ec0c025d
commit e15e88dad7
74 changed files with 207 additions and 207 deletions

View File

@@ -40,7 +40,7 @@ func main() {
註意這里的crawl所在的goroutine會將link作爲一個顯式的參數傳入來避免“循環變量快照”的問題(在5.6.1中有講解)。另外註意這里將命令行參數傳入worklist也是在一個另外的goroutine中進行的這是爲了避免在main goroutine和crawler goroutine中同時向另一個goroutine通過channel發送內容時發生死鎖(因爲另一邊的接收操作還沒有準備好)。當然這里我們也可以用buffered channel來解決問題這里不再贅述。
現在爬蟲可以高併發地運行起來併且可以産生一大坨的URL了不過還是會有倆問題。一個問題是在運行一段時間後可能會現在log的錯誤信息里的
現在爬蟲可以高併發地運行起來併且可以産生一大坨的URL了不過還是會有倆問題。一個問題是在運行一段時間後可能會現在log的錯誤信息里的
```
@@ -58,7 +58,7 @@ https://golang.org/blog/
```
最初的錯誤信息是一個讓人莫名的DNS査找失敗卽使這個域名是完全可靠的。而隨後的錯誤信息揭示了原因這個程序一次性創建了太多網絡連接超過了每一個進程的打開文件數限製旣而導致了在調用net.Dial像DNS査找失敗這樣的問題。
這個程序實在是太他媽併行了。無窮無盡地併行化併不是什麽好事情因爲不管怎麽説你的繫統總是會有一個些限製因素比如CPU覈心數會限製你的計算負載比如你的硬盤轉軸和磁頭數限製了你的本地磁盤IO操作頻率比如你的網絡帶寬限製了你的下載速度上限或者是你的一個web服務的服務容量上限等等。爲了解決這個問題我們可以限製併發程序所使用的資源來使之適應自己的運行環境。對於我們的例子來説最簡單的方法就是限製對links.Extract在同一時間最多不會有超過n次調用這里的n是fd的limit-20一般情況下。這個一個夜店里限製客人數目是一個道理隻有當有客人離開時會允許新的客人進入店內(譯註:作者你個老流氓)。
這個程序實在是太他媽併行了。無窮無盡地併行化併不是什麽好事情因爲不管怎麽説你的繫統總是會有一個些限製因素比如CPU覈心數會限製你的計算負載比如你的硬盤轉軸和磁頭數限製了你的本地磁盤IO操作頻率比如你的網絡帶寬限製了你的下載速度上限或者是你的一個web服務的服務容量上限等等。爲了解決這個問題我們可以限製併發程序所使用的資源來使之適應自己的運行環境。對於我們的例子來説最簡單的方法就是限製對links.Extract在同一時間最多不會有超過n次調用這里的n是fd的limit-20一般情況下。這個一個夜店里限製客人數目是一個道理隻有當有客人離開時會允許新的客人進入店內(譯註:作者你個老流氓)。
我們可以用一個有容量限製的buffered channel來控製併發這類似於操作繫統里的計數信號量概念。從概念上講channel里的n個空槽代表n個可以處理內容的token(通行證)從channel里接收一個值會釋放其中的一個token併且生成一個新的空槽位。這樣保證了在沒有接收介入時最多有n個發送操作。(這里可能我們拿channel里填充的槽來做token更直觀一些不過還是這樣吧~)。由於channel里的元素類型併不重要我們用一個零值的struct{}來作爲其元素。
@@ -82,7 +82,7 @@ func crawl(url string) []string {
}
```
第二個問題是這個程序永遠都不會終止,卽使它已經爬到了所有初始鏈接衍生的鏈接。(當然除非你慎重地選擇了合適的初始化URL或者已經實現了練習8.6中的深度限製,你應該還沒有意識到這個問題)。爲了使這個程序能夠終止我們需要在worklist爲空或者沒有crawl的goroutine在運行時退主循環。
第二個問題是這個程序永遠都不會終止,卽使它已經爬到了所有初始鏈接衍生的鏈接。(當然除非你慎重地選擇了合適的初始化URL或者已經實現了練習8.6中的深度限製,你應該還沒有意識到這個問題)。爲了使這個程序能夠終止我們需要在worklist爲空或者沒有crawl的goroutine在運行時退主循環。
```go
@@ -116,7 +116,7 @@ func main() {
這個版本中計算器n對worklist的發送操作數量進行了限製。每一次我們發現有元素需要被發送到worklist時我們都會對n進行++操作在向worklist中發送初始的命令行參數之前我們也進行過一次++操作。這里的操作++是在每啟動一個crawler的goroutine之前。主循環會在n減爲0時終止這時候説明沒活可榦了。
現在這個併發爬蟲會比5.6節中的深度優先蒐索版快上20倍而且不會什麽錯,併且在其完成任務時也會正確地終止。
現在這個併發爬蟲會比5.6節中的深度優先蒐索版快上20倍而且不會什麽錯,併且在其完成任務時也會正確地終止。
下面的程序是避免過度併發的另一種思路。這個版本使用了原來的crawl函數但沒有使用計數信號量取而代之用了20個長活的crawler goroutine這樣來保證最多20個HTTP請求在併發。
@@ -158,9 +158,9 @@ seen這個map被限定在main goroutine中也就是説這個map隻能在main
crawl函數爬到的鏈接在一個專有的goroutine中被發送到worklist中來避免死鎖。爲了節省空間這個例子的終止問題我們先不進行詳細闡述了。
練習8.6: 爲併發爬蟲增加深度限製。也就是説如果用戶設置了depth=3那麽隻有從首頁跳轉三次以內能夠跳到的頁面能被抓取到。
練習8.6: 爲併發爬蟲增加深度限製。也就是説如果用戶設置了depth=3那麽隻有從首頁跳轉三次以內能夠跳到的頁面能被抓取到。
練習8.7: 完成一個併發程序來創建一個線上網站的本地鏡像,把該站點的所有可達的頁面都抓取到本地硬盤。爲了省事,我們這里可以隻取現在該域下的所有頁面(比如golang.org結尾譯註外鏈的應該就不算了。)當然了,現在頁面里的鏈接你也需要進行一些處理,使其能夠在你的鏡像站點上進行跳轉,而不是指向原始的鏈接。
練習8.7: 完成一個併發程序來創建一個線上網站的本地鏡像,把該站點的所有可達的頁面都抓取到本地硬盤。爲了省事,我們這里可以隻取現在該域下的所有頁面(比如golang.org結尾譯註外鏈的應該就不算了。)當然了,現在頁面里的鏈接你也需要進行一些處理,使其能夠在你的鏡像站點上進行跳轉,而不是指向原始的鏈接。
譯註: