按这个例子来,只要改engine.go就好了
上次用waitgroup来并发感觉很慢,完全没有并发的效果。。。
下面用channel来并发,嗖的一下,超级快!!!
参考:https://chai2010.gitbooks.io/advanced-go-programming-book/content/ch1-basic/ch1-06-goroutine.html
engine.go改如下
package engine
import (
"fmt"
"log"
"runtime"
"time"
"crawler0.8/fetcher"
"crawler0.8/parser"
)
func Run(seed parser.Request) { //参数是一个种子
var requests []parser.Request //新建一个队列接收种子
requests = append(requests, seed) //接收参数传来的第一个种子
runtime.GOMAXPROCS(runtime.NumCPU())
jobChan := make(chan *parser.Request, 128) //创建job通道来存放Request
resultChan := make(chan *parser.ParserResult, 128) //创建result通道来存放ParserResult
jobChan <- &seed //发送第一个Request
start := time.Now()
count := 0
createWork(64, jobChan, resultChan) //创建工作池
go func(resultchan chan *parser.ParserResult) { // 开个打印ParserResult的协程
for result := range resultchan {
requests = append(requests, result.Requests...) //这里可以获取到很多url加到队列中
for _, item := range result.Items {
fmt.Printf("%s \n", item) //打印队列里的项目
}
count = count + 1
fmt.Printf("第%d个页面, 获取到%d条信息, time:%.2fs\n", count, len(result.Items), time.Since(start).Seconds())
}
}(resultChan)
for {
if len(requests) > 0 { //如果种子不位0
r := requests[0] //取第一个种子
requests = requests[1:] //截掉第一个种子
jobChan <- &r
}
time.Sleep(1 * time.Millisecond) //否则等一会让协程加点request先
}
}
func createWork(num int, jobChan chan *parser.Request, resultChan chan *parser.ParserResult) {
// 根据开协程个数,去跑运行
for i := 0; i < num; i++ {
go func(jobChan chan *parser.Request, resultChan chan *parser.ParserResult) {
for job := range jobChan {
contents, err := fetcher.Fetch(job.Url) //开始爬取对应种子的页面
if err != nil {
log.Printf("Fetching %s error!", job.Url)
continue //如果获取失败就继续下一个种子
}
parserResult := job.ParserFunc(contents) //队列结构的函数提取结果
//运算结果扔到管道
resultChan <- &parserResult
}
}(jobChan, resultChan)
}
}