mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-01 02:28:56 +00:00
good good study, day day up!
This commit is contained in:
parent
cef210b584
commit
e31c406b5d
@ -3,10 +3,13 @@
|
|||||||
|
|
||||||
*大傢幫助完善, 請保證列錶有序(忽略大小寫)!*
|
*大傢幫助完善, 請保證列錶有序(忽略大小寫)!*
|
||||||
|
|
||||||
```
|
|
||||||
chai2010 <chaishushan@gmail.com>
|
作者 | 章節
|
||||||
Xargin <cao1988228@163.com>
|
-------------------------------------- | -------------------------
|
||||||
```
|
`chai2010 <chaishushan@gmail.com>` | 前言/第二章/第十章/第十三章
|
||||||
|
`Xargin <cao1988228@163.com>` | 第一章/第六章/第八章
|
||||||
|
`foreversmart <njutree@gmail.com>` | 第七章(0和1節)
|
||||||
|
`CrazySssst` | 第七章(0節)
|
||||||
|
|
||||||
# 版權
|
# 版權
|
||||||
|
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
作為 [《The Go Programming Language》](http://gopl.io/) (中文名[《Go編程語言》](http://golang-china.github.io/gopl-zh)) 英文原版紙質圖書的購買者, [《Go聖經讀書筆記》](http://golang-china.github.io/gopl-zh) 是我們的 **讀書筆記** 和 **習題解答**, 僅供學習交流用.
|
作為 [《The Go Programming Language》](http://gopl.io/) (中文名[《Go編程語言》](http://golang-china.github.io/gopl-zh)) 英文原版紙質圖書的購買者, [《Go聖經讀書筆記》](http://golang-china.github.io/gopl-zh) 是我們的 **讀書筆記** 和 **習題解答**, 僅供學習交流用.
|
||||||
|
|
||||||
- 此中文版 **讀書筆記** 在綫預覽: http://golang-china.github.io/gopl-zh
|
- 此 **讀書筆記** 在綫預覽: http://golang-china.github.io/gopl-zh
|
||||||
- 此中文版 **讀書筆記** 的源文件: http://github.com/golang-china/gopl-zh
|
- 此 **讀書筆記** 的源文件: http://github.com/golang-china/gopl-zh
|
||||||
- 此中文版 **讀書筆記** 項目進度: http://github.com/golang-china/gopl-zh/blob/master/progress.md
|
- 此 **讀書筆記** 項目進度: http://github.com/golang-china/gopl-zh/blob/master/progress.md
|
||||||
|
- 此 **讀書筆記** 蔘與人員: http://github.com/golang-china/gopl-zh/blob/master/CONTRIBUTORS.md
|
||||||
- 原版官網: http://gopl.io
|
- 原版官網: http://gopl.io
|
||||||
|
|
||||||
[![](cover_small.jpg)](https://github.com/golang-china/gopl-zh)
|
[![](cover_small.jpg)](https://github.com/golang-china/gopl-zh)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
## 8.6. 示例: 併髮的Web爬蟲
|
## 8.6. 示例: 併髮的Web爬蟲
|
||||||
|
|
||||||
在5.6节中,我们做了一个简单的web爬虫,用bfs(广度优先)算法来抓取整个网站。在本节中,我们会让这个这个爬虫并行化,这样每一个彼此独立的抓取命令可以并行进行IO,最大化利用网络资源。crawl函数和gopl.io/ch5/findlinks3中的是一样的。
|
在5.6節中,我們做了一箇簡單的web爬蟲,用bfs(廣度優先)算法來抓取整箇網站。在本節中,我們會讓這箇這箇爬蟲併行化,這樣每一箇彼此獨立的抓取命令可以併行進行IO,最大化利用網絡資源。crawl函數和gopl.io/ch5/findlinks3中的是一樣的。
|
||||||
|
|
||||||
```go
|
```go
|
||||||
gopl.io/ch8/crawl1
|
gopl.io/ch8/crawl1
|
||||||
@ -14,7 +14,7 @@ func crawl(url string) []string {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
主函数和5.6节中的breadthFirst(深度优先)类似。像之前一样,一个worklist是一个记录了需要处理的元素的队列,每一个元素都是一个需要抓取的URL列表,不过这一次我们用channel代替slice来做这个队列。每一个对crawl的调用都会在他们自己的goroutine中进行并且会把他们抓到的链接发送回worklist。
|
主函數和5.6節中的breadthFirst(深度優先)類似。像之前一樣,一箇worklist是一箇記録了需要處理的元素的隊列,每一箇元素都是一箇需要抓取的URL列錶,不過這一次我們用channel代替slice來做這箇隊列。每一箇對crawl的調用都會在他們自己的goroutine中進行併且會把他們抓到的鏈接髮送迴worklist。
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
@ -38,9 +38,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
注意这里的crawl所在的goroutine会将link作为一个显式的参数传入,来避免“循环变量快照”的问题(在5.6.1中有讲解)。另外注意这里将命令行参数传入worklist也是在一个另外的goroutine中进行的,这是为了避免在main goroutine和crawler goroutine中同时向另一个goroutine通过channel发送内容时发生死锁(因为另一边的接收操作还没有准备好)。当然,这里我们也可以用buffered channel来解决问题,这里不再赘述。
|
註意這裏的crawl所在的goroutine會將link作為一箇顯式的蔘數傳入,來避免“循環變量快照”的問題(在5.6.1中有講解)。另外註意這裏將命令行蔘數傳入worklist也是在一箇另外的goroutine中進行的,這是為了避免在main goroutine和crawler goroutine中衕時嚮另一箇goroutine通過channel髮送內容時髮生死鎖(因為另一邊的接收操作還沒有準備好)。噹然,這裏我們也可以用buffered channel來解決問題,這裏不再贅述。
|
||||||
|
|
||||||
现在爬虫可以高并发地运行起来,并且可以产生一大坨的URL了,不过还是会有俩问题。一个问题是在运行一段时间后可能会出现在log的错误信息里的:
|
現在爬蟲可以高併髮地運行起來,併且可以產生一大坨的URL了,不過還是會有倆問題。一箇問題是在運行一段時間後可能會齣現在log的錯誤信息裏的:
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -56,13 +56,13 @@ https://golang.org/blog/
|
|||||||
too many open files
|
too many open files
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
最初的错误信息是一个让人莫名的DNS查找失败,即使这个域名是完全可靠的。而随后的错误信息揭示了原因:这个程序一次性创建了太多网络连接,超过了每一个进程的打开文件数限制,既而导致了在调用net.Dial像DNS查找失败这样的问题。
|
最初的錯誤信息是一箇讓人莫名的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{}来作为其元素。
|
我們可以用一箇有容量限製的buffered channel來控製併髮,這類似於操作係統裏的計數信號量概唸。從概唸上講,channel裏的n箇空槽代錶n箇可以處理內容的token(通行証),從channel裏接收一箇值會釋放其中的一箇token,併且生成一箇新的空槽位。這樣保証了在沒有接收介入時最多有n箇髮送操作。(這裏可能我們拿channel裏填充的槽來做token更直觀一些,不過還是這樣吧~)。由於channel裏的元素類型併不重要,我們用一箇零值的struct{}來作為其元素。
|
||||||
|
|
||||||
让我们重写crawl函数,将对links.Extract的调用操作用获取、释放token的操作包裹起来,来确保同一时间对其只有20个调用。信号量数量和其能操作的IO资源数量应保持接近。
|
讓我們重寫crawl函數,將對links.Extract的調用操作用穫取、釋放token的操作包裹起來,來確保衕一時間對其隻有20箇調用。信號量數量和其能操作的IO資源數量應保持接近。
|
||||||
|
|
||||||
```go
|
```go
|
||||||
gopl.io/ch8/crawl2
|
gopl.io/ch8/crawl2
|
||||||
@ -114,11 +114,11 @@ func main() {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
这个版本中,计算器n对worklist的发送操作数量进行了限制。每一次我们发现有元素需要被发送到worklist时,我们都会对n进行++操作,在向worklist中发送初始的命令行参数之前,我们也进行过一次++操作。这里的操作++是在每启动一个crawler的goroutine之前。主循环会在n减为0时终止,这时候说明没活可干了。
|
這箇版本中,計算器n對worklist的髮送操作數量進行了限製。每一次我們髮現有元素需要被髮送到worklist時,我們都會對n進行++操作,在嚮worklist中髮送初始的命令行蔘數之前,我們也進行過一次++操作。這裏的操作++是在每啓動一箇crawler的goroutine之前。主循環會在n減為0時終止,這時候説明沒活可乾了。
|
||||||
|
|
||||||
现在这个并发爬虫会比5.6节中的深度优先搜索版快上20倍,而且不会出什么错,并且在其完成任务时也会正确地终止。
|
現在這箇併髮爬蟲會比5.6節中的深度優先蒐索版快上20倍,而且不會齣什麼錯,併且在其完成任務時也會正確地終止。
|
||||||
|
|
||||||
下面的程序是避免过度并发的另一种思路。这个版本使用了原来的crawl函数,但没有使用计数信号量,取而代之用了20个长活的crawler goroutine,这样来保证最多20个HTTP请求在并发。
|
下麪的程序是避免過度併髮的另一種思路。這箇版本使用了原來的crawl函數,但沒有使用計數信號量,取而代之用了20箇長活的crawler goroutine,這樣來保証最多20箇HTTP請求在併髮。
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
@ -152,17 +152,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
所有的爬虫goroutine现在都是被同一个channel-unseenLinks喂饱的了。主goroutine负责拆分它从worklist里拿到的元素,然后把没有抓过的经由unseenLinks channel发送给一个爬虫的goroutine。
|
所有的爬蟲goroutine現在都是被衕一箇channel-unseenLinks餵飽的了。主goroutine負責拆分它從worklist裏拿到的元素,然後把沒有抓過的經由unseenLinks channel髮送給一箇爬蟲的goroutine。
|
||||||
|
|
||||||
seen这个map被限定在main goroutine中;也就是说这个map只能在main goroutine中进行访问。类似于其它的信息隐藏方式,这样的约束可以让我们从一定程度上保证程序的正确性。例如,内部变量不能够在函数外部被访问到;变量(§2.3.4)在没有被转义的情况下是无法在函数外部访问的;一个对象的封装字段无法被该对象的方法以外的方法访问到。在所有的情况下,信息隐藏都可以帮助我们约束我们的程序,使其不发生意料之外的情况。
|
seen這箇map被限定在main goroutine中;也就是説這箇map隻能在main goroutine中進行訪問。類似於其它的信息隱藏方式,這樣的約束可以讓我們從一定程度上保証程序的正確性。例如,內部變量不能夠在函數外部被訪問到;變量(§2.3.4)在沒有被轉義的情況下是無法在函數外部訪問的;一箇對象的封裝字段無法被該對象的方法以外的方法訪問到。在所有的情況下,信息隱藏都可以幫助我們約束我們的程序,使其不髮生意料之外的情況。
|
||||||
|
|
||||||
crawl函数爬到的链接在一个专有的goroutine中被发送到worklist中来避免死锁。为了节省空间,这个例子的终止问题我们先不进行详细阐述了。
|
crawl函數爬到的鏈接在一箇專有的goroutine中被髮送到worklist中來避免死鎖。為了節省空間,這箇例子的終止問題我們先不進行詳細闡述了。
|
||||||
|
|
||||||
练习8.6: 为并发爬虫增加深度限制。也就是说,如果用户设置了depth=3,那么只有从首页跳转三次以内能够跳到的页面才能被抓取到。
|
練習8.6: 為併髮爬蟲增加深度限製。也就是説,如果用戶設置了depth=3,那麼隻有從首頁跳轉三次以內能夠跳到的頁麪纔能被抓取到。
|
||||||
|
|
||||||
练习8.7: 完成一个并发程序来创建一个线上网站的本地镜像,把该站点的所有可达的页面都抓取到本地硬盘。为了省事,我们这里可以只取出现在该域下的所有页面(比如golang.org结尾,译注:外链的应该就不算了。)当然了,出现在页面里的链接你也需要进行一些处理,使其能够在你的镜像站点上进行跳转,而不是指向原始的链接。
|
練習8.7: 完成一箇併髮程序來創建一箇線上網站的本地鏡像,把該站點的所有可達的頁麪都抓取到本地硬盤。為了省事,我們這裏可以隻取齣現在該域下的所有頁麪(比如golang.org結尾,譯註:外鏈的應該就不算了。)噹然了,齣現在頁麪裏的鏈接你也需要進行一些處理,使其能夠在你的鏡像站點上進行跳轉,而不是指嚮原始的鏈接。
|
||||||
|
|
||||||
|
|
||||||
译注:
|
譯註:
|
||||||
拓展阅读:
|
拓展閱讀:
|
||||||
http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/
|
http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
作為 [《The Go Programming Language》](http://gopl.io/) (中文名[《Go編程語言》](http://golang-china.github.io/gopl-zh)) 英文原版紙質圖書的購買者, [《Go聖經讀書筆記》](http://golang-china.github.io/gopl-zh) 是我們的 **讀書筆記** 和 **習題解答**, 僅供學習交流用.
|
作為 [《The Go Programming Language》](http://gopl.io/) (中文名[《Go編程語言》](http://golang-china.github.io/gopl-zh)) 英文原版紙質圖書的購買者, [《Go聖經讀書筆記》](http://golang-china.github.io/gopl-zh) 是我們的 **讀書筆記** 和 **習題解答**, 僅供學習交流用.
|
||||||
|
|
||||||
- 此中文版 **讀書筆記** 在綫預覽: http://golang-china.github.io/gopl-zh
|
- 此 **讀書筆記** 在綫預覽: http://golang-china.github.io/gopl-zh
|
||||||
- 此中文版 **讀書筆記** 的源文件: http://github.com/golang-china/gopl-zh
|
- 此 **讀書筆記** 的源文件: http://github.com/golang-china/gopl-zh
|
||||||
- 此中文版 **讀書筆記** 項目進度: http://github.com/golang-china/gopl-zh/blob/master/progress.md
|
- 此 **讀書筆記** 項目進度: http://github.com/golang-china/gopl-zh/blob/master/progress.md
|
||||||
|
- 此 **讀書筆記** 蔘與人員: http://github.com/golang-china/gopl-zh/blob/master/CONTRIBUTORS.md
|
||||||
- 原版官網: http://gopl.io
|
- 原版官網: http://gopl.io
|
||||||
|
|
||||||
[![](cover_small.jpg)](https://github.com/golang-china/gopl-zh)
|
[![](cover_small.jpg)](https://github.com/golang-china/gopl-zh)
|
||||||
|
Loading…
Reference in New Issue
Block a user