【问题标题】:How to handle HTTP timeout errors and accessing status codes in golang如何处理 HTTP 超时错误和访问 golang 中的状态码
【发布时间】:2015-08-16 18:22:18
【问题描述】:

我有一些用 Go 编写的代码(见下文),它应该“扇出”HTTP 请求,然后整理/汇总详细信息。

我是 golang 的新手,所以希望我成为一个 nOOb 并且我的知识是有限的

程序的输出目前是这样的:

{
    "Status":"success",
    "Components":[
        {"Id":"foo","Status":200,"Body":"..."},
        {"Id":"bar","Status":200,"Body":"..."}, 
        {"Id":"baz","Status":404,"Body":"..."}, 
        ...
    ]
}

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

我现在的问题是如何最好地处理这些错误,特别是“超时”相关的错误;因为我不确定如何识别失败是超时还是其他错误?

目前我一直收到一个全面错误:

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

http://localhost:8080/pugs 通常是失败的 url(希望是超时!)。但是正如您从代码(如下)中看到的那样,我不确定如何确定错误代码与超时有关,也不确定如何访问响应的状态代码(我目前只是将其设置为@987654326 @ 但显然这是不对的 - 如果服务器出错,我会期待像 500 这样的状态代码,显然我想在我发回的汇总响应中反映这一点。

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

    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\n", 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\n", 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\n", err)
                    return
            }

            fmt.Println(string(j))
    }

【问题讨论】:

  • 很可能与您的问题无关,但您有一个附加到cr 的数据竞争。你不能在没有同步的情况下从多个 goroutine 中写入相同的变量。您可能希望使用race detector 构建/运行。
  • 感谢您的评论。我会改用频道+我会调查那个种族检测器:-)
  • 如果客户端调用返回错误,则没有状态码,因为没有完整的http请求。那时你对错误无能为力,但在 go1.5 中,client.Timeout 至少会在 net.Error 中返回更好的消息。
  • this 会帮忙吗?
  • 似乎 1.5 的发布日期已过期(截至 2015 年 8 月 17 日)。我将不得不坚持看看这是否确实解决了问题。

标签: go timeout http-status-codes


【解决方案1】:

我添加这个是为了完成,因为正确答案是 Dave C 在接受答案的 cmets 中提供的。

我们可以尝试将错误转换为 net.Error 并检查是否超时。

resp, err := client.Get(url)
if err != nil {
    // if there is an error check if its a timeout error
    if e, ok := err.(net.Error); ok && e.Timeout() {
        // handle timeout
           return
    } 
    // otherwise handle other types of error
}

【讨论】:

  • 谢谢。同意。我删除了我原来选择的答案,但我不让我选择这个作为新答案吗?
  • @Integralist,你有收到任何警告通知吗?
【解决方案2】:

如果你想扇出然后聚合结果并且你想要特定的超时行为 net/http 包没有给你,那么你可能想要使用 goroutines 和通道。

我今天刚刚观看了这个视频,它将带您了解使用 Go 的并发特性的那些场景。另外,演讲者 Rob Pike 很有权威——他解释得比我好。

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

【讨论】:

  • 投反对票是为了发布一小时长的视频。答案在哪里?
【解决方案3】:

Go 1.5 版本通过更具体地说明它处理的错误类型解决了这个问题。

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

status := checkError(err.Error())

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

    if timeout {
        return 408
    }

  return 500
}

【讨论】:

  • 不要在 best 的错误字符串中摸索,它非常脆弱;错误字符串是 not 任何 API 的一部分。 net.Error interface 为此定义了 Timeout 方法。
  • @Dave C 感谢您的评论,但我不确定您的建议是如何工作的。我从 client.Get 返回的错误是一个字符串。它没有方法。所以没有什么可查询的
  • 这不仅仅是一个字符串,它是一个error。通过使用err.Error() 调用您的检查函数,您可以清除除格式化结果字符串之外的所有内容。相反,您应该尝试类型断言和其他检查。只有当所有这些都不起作用时,您才应该 a) 提交关于不可测试错误的错误报告和 b) 作为丑陋的解决方法(带有很大的警告注释)可能在字符串(或更改您调用的代码以返回可测试的错误并将其作为更改请求提交到上游)。
  • 顺便说一句,我终于开始尝试类型断言检查(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…*/} 之类的东西会正确地说这不是与网络超时相关的错误。)
猜你喜欢
  • 1970-01-01
  • 2012-09-25
  • 1970-01-01
  • 2019-09-18
  • 1970-01-01
  • 2011-05-21
  • 2020-07-05
  • 2020-07-14
  • 2011-05-22
相关资源
最近更新 更多