在Go中发送多个请求时出现零星的EOF错误

时间:2016-06-09 13:31:08

标签: go tcp dns udp

首先让我说我是Go的新手,欢迎任何指示/更正。

我正在尝试编写一个小型Go应用程序,它将创建许多DNS查询并在各自的Go例程中发送它们。我从1000个URL的文件中获取URL并创建切片,对于切片中的每个URL,我查询A记录,如果成功并且如果有错误,则将URL和经过的时间推送到结果通道相应的错误通道。然后我听一下选择。

我正在努力解决的问题是,在使用TCP时,我会收到(看似随机)某些查询的EOF错误,并且在使用UDP时会收到i / o超时错误。我想使用TCP并确保响应,我很遗憾为什么我会得到一个EOF错误。如果我对相同的URL和1000个不同的URL进行1000次查询,也会发生这种情况

使用OSX和Go版本1.6

这是我到目前为止所做的:

package main

import (
    "bufio"
    "fmt"
    "github.com/miekg/dns"
    "os"
    "time"
)

// CHECK AND PRINT ERRORS
func checkErr(e error) {
    if e != nil {
        fmt.Println("Error: %s", e)
    }
}

// MAKES A SLICE OF URLS FROM TXT FILE
func urlSlice() []string {
    result := []string{}
    file, err := os.Open("topsites.txt")
    checkErr(err)
    defer file.Close()
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        result = append(result, scanner.Text())
    }
    return result
}

func makeQuery(target string) (string, error) {
    server := "8.8.8.8"
    // WILL RECIEVE EOF ERROR IF TCP - I/O TIMEOUT IF UDP
    c := dns.Client{Net: "tcp", Timeout: time.Duration(100 * time.Second)}
    m := dns.Msg{}
    m.SetQuestion(dns.Fqdn(target+"."), dns.TypeA)

    _, t, err := c.Exchange(&m, server+":53")
    if err != nil {
        return "", err
    }
    return "Url: " + target + " - - Took: " + t.String(), nil
}

func main() {
    start := time.Now()
    targets := urlSlice()
    resch, errch := make(chan string), make(chan error)
    for _, url := range targets {
        go func(url string) {
            res, err := makeQuery(url)
            if err != nil {
                errch <- err
                return
            }
            resch <- res
        }(url)
    }

    for i := 0; i < len(targets); i++ {
        select {
        case res := <-resch:
            fmt.Println(res)
        case err := <-errch:
            fmt.Println(err)
        }
    }
    elapsed := time.Since(start)
    fmt.Println("\ntotal time elapsed: ", elapsed)
}

输出:

Url: facebook.com - - Took: 548.582107ms
Url: wordpress.com - - Took: 548.805505ms
Url: google.com.br - - Took: 541.491363ms
Url: onclickads.net - - Took: 548.16544ms
Url: bongacams.com - - Took: 543.28688ms
Url: tianya.cn - - Took: 543.41525ms
Url: blogger.com - - Took: 544.461005ms
Url: alibaba.com - - Took: 543.53541ms
Url: gmw.cn - - Took: 543.56093ms
Url: pornhub.com - - Took: 664.297282ms
Url: outbrain.com - - Took: 664.423217ms
Url: ask.com - - Took: 671.557037ms
EOF
EOF
EOF
EOF
EOF
EOF
EOF
Url: t.co - - Took: 1.166130918s
Url: youth.cn - - Took: 1.946658912s
Url: apple.com - - Took: 2.763568935s

...continued...

total time elapsed:  23.703546858s

思想,建议和帮助都表示赞赏。谢谢!

1 个答案:

答案 0 :(得分:0)

这可能是有意义的:https://idea.popcount.org/2013-11-28-how-to-resolve-a-million-domains/

根据文章,天真的方法受限于可以同时打开多少UDP套接字 - 大约1000.我会假设TCP套接字也是如此 - 你将用完文件描述符或其他一些资源。

作者在文章末尾提供了他的并行dns解析器的链接:https://github.com/majek/goplayground/blob/master/resolve/resolve.go

他使用可配置数量的go-routines,它们都通过同一个UDP端口进行通信。我猜你是否想要使用TCP,你必须每个例行程序使用1个tcp连接(go-routines的数量远低于1000)