我有一些用Go编写的代码(见下文),该代码应该"散布" HTTP请求,并整理/聚合详细信息。

I'm new to golang and so expect me to be a nOOb and my knowledge to be limited

程序的输出当前类似于:

1
2
3
4
5
6
7
8
9
{
   "Status":"success",
   "Components":[
        {"Id":"foo","Status":200,"Body":"..."},
        {"Id":"bar","Status":200,"Body":"..."},
        {"Id":"baz","Status":404,"Body":"..."},
        ...
    ]
}

有一个本地服务器正在运行,该服务器故意过慢(睡眠5秒钟,然后返回响应)。但是我列出了其他站点(请参见下面的代码),它们有时也会触发错误(如果它们出错,那就很好了)。

我目前遇到的问题是如何最好地处理这些错误,尤其是与"超时"相关的错误。因为我不确定如何识别失败是超时还是其他错误?

此刻,我一直都返回错误消息:

1
Get http://localhost:8080/pugs: read tcp 127.0.0.1:8080: use of closed network connection

其中http://localhost:8080/pugs通常是失败的URL(希望是超时!)。但是,从下面的代码中可以看到,我不确定如何确定错误代码是否与超时相关,也不确定如何访问响应的状态代码(我目前不确定是否将其设置为,但显然这是不对的-如果服务器出错,则我期望类似500状态代码的内容,并且显然我想在我发回的汇总响应中反映出来。

完整的代码可以在下面看到。任何帮助表示赞赏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    package main

    import (
           "encoding/json"
           "fmt"
           "io/ioutil"
           "net/http"
           "sync"
           "time"
    )

    type Component struct {
            Id  string `json:"id"`
            Url string `json:"url"`
    }

    type ComponentsList struct {
            Components []Component `json:"components"`
    }

    type ComponentResponse struct {
            Id     string
            Status int
            Body   string
    }

    type Result struct {
            Status     string
            Components []ComponentResponse
    }

    var overallStatus string ="success"

    func main() {
            var cr []ComponentResponse
            var c ComponentsList

            b := []byte(`{"components":[{"id":"local","url":"http://localhost:8080/pugs"},{"id":"google","url":"http://google.com/"},{"id":"integralist","url":"http://integralist.co.uk/"},{"id":"sloooow","url":"http://stevesouders.com/cuzillion/?c0=hj1hfff30_5_f&t=1439194716962"}]}`)

            json.Unmarshal(b, &c)

            var wg sync.WaitGroup

            timeout := time.Duration(1 * time.Second)
            client := http.Client{
                    Timeout: timeout,
            }

            for i, v := range c.Components {
                    wg.Add(1)

                    go func(i int, v Component) {
                            defer wg.Done()

                            resp, err := client.Get(v.Url)

                            if err != nil {
                                fmt.Printf("Problem getting the response: %s\
", err)

                                cr = append(cr, ComponentResponse{
                                    v.Id,
                                    404,
                                    err.Error(),
                                })
                            } else {
                                    defer resp.Body.Close()
                                    contents, err := ioutil.ReadAll(resp.Body)
                                    if err != nil {
                                            fmt.Printf("Problem reading the body: %s\
", err)
                                    }

                                    cr = append(cr, ComponentResponse{
                                            v.Id,
                                            resp.StatusCode,
                                            string(contents),
                                    })
                            }
                    }(i, v)
            }
            wg.Wait()

            j, err := json.Marshal(Result{overallStatus, cr})
            if err != nil {
                    fmt.Printf("Problem converting to JSON: %s\
", err)
                    return
            }

            fmt.Println(string(j))
    }
  • 最有可能与您的问题无关,但是您将数据竞争附加到cr。 如果没有同步,则无法从多个goroutine中写入相同的变量。 您可能要使用种族探测器来构建/运行。
  • 感谢您的评论。 我将寻找利用渠道来代替+虐待调查该种族探测器:-)
  • 如果客户端调用返回错误,则没有状态代码,因为没有完整的http请求。 那时您没有太多可以做的错误,但是在go1.5中,client.Timeout至少会在net.Error中返回更好的消息。
  • 请问有帮助吗?
  • 似乎1.5的发布日期已过期(截至2015年8月17日)。 病态必须坚持下去,看看是否确实可以解决问题。

如果您想散开然后汇总结果,并且想要net / http软件包没有给您的特定超时行为,那么您可能要使用goroutine和通道。

我今天刚刚看了这段视频,它将通过Go的并发功能向您详细介绍这些场景。 另外,发言人罗伯·派克(Rob Pike)很有权威-他的解释比我能做的更好。

https://www.youtube.com/watch?v=f6kdp27TYZs

  • downvote用于发布一个小时的视频。答案在哪里?

Go 1.5版本通过更具体地处理错误类型来解决此问题。

因此,如果您看到此示例https://github.com/Integralist/Go-Requester/blob/master/requester.go#L38,您将看到我可以将正则表达式模式应用于错误消息以解密是否 错误的确是超时

1
2
3
4
5
6
7
8
9
10
11
status := checkError(err.Error())

func checkError(msg string) int {
    timeout, _ := regexp.MatchString("Timeout", msg)

    if timeout {
        return 408
    }

  return 500
}
  • 最好不要在错误字符串中四处张望,它是令人难以置信的脆弱。错误字符串不属于任何API。 net.Error接口为此定义了Timeout方法。
  • @Dave C感谢您的评论,但我不确定您的建议如何工作。我从client.Get返回的错误是一个字符串。它没有方法。没什么可查询的
  • 它不仅是字符串,而且是error。通过使用err.Error()调用check函数,可以遍历除格式化结果字符串以外的所有内容。相反,您应该尝试类型声明和其他检查。仅当所有方法均无效时,您才可以:a)提交有关不可测试错误的错误报告,以及b)作为丑陋的解决方法(带有大的警告注释),可以在字符串中使用grep(或更改您调用的代码)返回可测试的错误,并将其作为更改请求提交给上游)。
  • 顺便说一句,我终于开始尝试类型断言检查(err.(net.Error)),似乎错误的类型是* url.Error而不是net.Error,所以我不能使用该; panic: interface conversion: *url.Error is not net.Error: missing method Temporary
  • url.Error包含一个带有基本错误(如果有)的Err error字段。它几乎还专门用于URL解析错误,在这种情况下,它永远不会超时。 (例如,类似if e, ok := err.(net.Error); ok && e.Timeout() {*…network timeout…*}之类的东西会正确地说出它不是与网络超时相关的错误。)