1. yoheimiyamoto

    Posted

    yoheimiyamoto
Changes in title
+クローリングの平行処理してベンチマーク取ってみました。
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,197 @@
+# はじめに
+[agouti](https://github.com/sclevine/agouti) を使ってクローリングを行い、ベンチマークを測定してみました。
+
+# クローリング対象ページ
+今回クローリング対象のページは、goのtemplateパッケージを使用してローカルに生成したものを利用しました。以下では9つのページを生成しています。
+
+```
+func createPages() {
+ createPage("/1", []string{"/2", "/3"})
+ createPage("/2", []string{"/4", "/5"})
+ createPage("/3", []string{"/6", "/7"})
+ createPage("/4", []string{"/8", "/9"})
+ createPage("/5", nil)
+ createPage("/6", nil)
+ createPage("/7", nil)
+ createPage("/8", nil)
+ createPage("/9", nil)
+
+ http.ListenAndServe(":8080", nil)
+}
+
+func createPage(url string, links []string) {
+ handler := newHandler(links)
+ http.HandleFunc(url, handler)
+}
+
+func newHandler(links []string) func(w http.ResponseWriter, r *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ temp := "template.html.tmpl"
+ t := template.Must(template.New(temp).ParseFiles(temp))
+ if err := t.Execute(w, links); err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+```
+
+## template.html.tmpl
+```
+<!DOCTYPE html>
+<html>
+<body>
+ <div>
+ {{range .}}
+ <p><a href="{{.}}">{{.}}</a></p>
+ {{end}}
+ </div>
+</body>
+</html>
+```
+
+# クローラー
+再起的にクローリングを行いページ内のリンクを取得するクローラーを作成しました。
+
+```
+// maxConcurrent = クローリング最大平行数
+// maxCrawlCount = クローリングするページの最大数
+func Crawls(link string, maxConcurrent int, maxCrawlCount int) ([]string, error) {
+ errCh := make(chan error)
+
+ go func() {
+ worklinks <- link
+ }()
+
+ for i := 0; i < maxConcurrent; i++ {
+ go func() {
+ for link := range unseen {
+ links, err := crawl(link)
+ if err != nil {
+ errCh <- err
+ return
+ }
+ for _, l := range links {
+ l := l
+ go func() {
+ worklinks <- l
+ }()
+ }
+ }
+ }()
+ }
+
+ for link := range worklinks {
+ if !seen[link] {
+ seen[link] = true
+ unseen <- link
+ }
+ if len(seen) >= maxCrawlCount {
+ links := make([]string, 0, len(seen))
+ for k := range seen {
+ links = append(links, k)
+ }
+ return links, nil
+ }
+ }
+
+ return nil, nil
+}
+
+// ページ内のリンクを検索
+func crawl(link string) ([]string, error) {
+ driver := agouti.ChromeDriver(
+ agouti.ChromeOptions("args", []string{
+ "--headless",
+ }),
+ )
+ if err := driver.Start(); err != nil {
+ return nil, errors.Wrap(err, "Failed to start driver")
+ }
+ defer driver.Stop()
+
+ p, err := driver.NewPage()
+ if err != nil {
+ return nil, err
+ }
+
+ err = p.Navigate(link)
+ if err != nil {
+ return nil, err
+ }
+
+ t := p.All("a")
+ length, err := t.Count()
+ if err != nil {
+ return nil, err
+ }
+
+ links := []string{}
+ for i := 0; i < length; i++ {
+ link, err := t.At(i).Attribute("href")
+ if err != nil {
+ return nil, err
+ }
+ links = append(links, link)
+ }
+ return links, nil
+}
+```
+
+# Benchmark測定
+最大平行数を変えてベンチマークを測定してみました。現在の実装だと平行数を単純に増やせばその分早くなるというわけではないことがわかりました。。今後改善していきたいと思います。
+
+|平行数|処理時間(ns)|
+|:--|:--|
+|1|16479309835|
+|2|10561147824|
+|3|8914031897|
+|4|8981436252|
+|5|7767930345|
+|10|6496013517|
+|50|8146189396|
+
+
+```
+func BenchmarkCrawl1(b *testing.B) {
+ benchmark(b, 1)
+}
+
+func BenchmarkCrawl2(b *testing.B) {
+ benchmark(b, 2)
+}
+
+func BenchmarkCrawl3(b *testing.B) {
+ benchmark(b, 3)
+}
+
+func BenchmarkCrawl4(b *testing.B) {
+ benchmark(b, 4)
+}
+
+func BenchmarkCrawl5(b *testing.B) {
+ benchmark(b, 5)
+}
+
+func BenchmarkCrawl10(b *testing.B) {
+ benchmark(b, 10)
+}
+
+func BenchmarkCrawl10(b *testing.B) {
+ benchmark(b, 50)
+}
+
+func benchmark(b *testing.B, maxConcurrent int) {
+ go func() {
+ createPages()
+ }()
+
+ for i := 0; i < b.N; i++ {
+ links, err := Crawls("http://localhost:8080/1", maxConcurrent, 9)
+ b.Logf("links: %v", links)
+ if err != nil {
+ b.Error(err)
+ return
+ }
+ }
+}
+```