【问题标题】:Timeout in goroutines and Http requestsgoroutine 和 Http 请求中的超时
【发布时间】:2014-02-26 13:22:17
【问题描述】:

我正在检查服务器的状态。服务器的睡眠时间超过 15 秒,我正在检查超时。

package main

import (
    "fmt"
    "net/http"
    "time"
)

var urls = []string{
    "http://site-centos-64:8080/examples/abc1.jsp",
    }

type HttpResponse struct {
    url      string
    response *http.Response
    err      error
}
var ch = make(chan *HttpResponse, 100) // buffered
var count int   
func asyncHttpGets(urls []string) []*HttpResponse {
    responses := []*HttpResponse{}
    count:=0
    timeout := make(chan bool, 100)
    for i:=0;i<500;i++{
        go func(){
                for _, url := range urls {
                    resp, err := http.Get(url)
                    count++;
                    go func() {
                    time.Sleep(1 * time.Second)
                    timeout <- true
                    }() 
                    ch <- &HttpResponse{url, resp, err}
                    if err != nil {
                        return
                    }
                    resp.Body.Close()
                }
        }()
    }
    for {
        select {
        case r := <-ch:
            responses = append(responses, r)
            if count == 500 {
                return responses
            }
        case <-timeout:
                fmt.Println("Timed Out")
                if count == 500 {
                return responses
            }
        }
    }
    return responses

}

func main() {
    now:=time.Now()
    results := asyncHttpGets(urls)
    for _, result := range results {
        fmt.Printf("%s status: %s\n", result.url,result.response.Status)
    }
    fmt.Println( time.Since(now))
}

但是发生的情况是,最初它会打印“超时”,但最后的 150-200 个请求显示它不应该显示的“200 OK”状态。此外,当尝试执行 1000 次时,它会显示“恐慌:运行时错误:无效的内存地址或 nil 指针取消引用”

【问题讨论】:

    标签: go timeout goroutine


    【解决方案1】:

    在启动超时 goroutine 之前,您正在执行 resp, err := http.Get(url)。 这将导致一切阻塞,直到响应准备好,然后同时在两个通道上发送。

    只需将启动超时 goroutine 移动到发送请求之前的行,就可以了。即:

      for _, url := range urls {
    
    
                    go func() {
                       time.Sleep(1 * time.Second)
                       timeout <- true
                       count++;
                    }() 
    
                    resp, err := http.Get(url)
                    count++; //I think this is what you meant, right?
                    ch <- &HttpResponse{url, resp, err}
                    if err != nil {
                        return
                    }
                    resp.Body.Close()
                }
    

    顺便说一句,尝试使用原子增量进行计数,并且可能使用等待组和time.After 通道而不是睡眠。

    【讨论】:

    • 计时器现在工作正常。 Used time.After。你能告诉我为什么当我检查状态大约 1000 次时它会显示错误。
    • 当请求数超过1000时显示“panic: runtime error: invalid memory address or nil pointer dereference”。
    • 问题中的代码存在更多基本问题。例如,一旦超时触发,就没有什么可以停止 HTTP 请求(或者相反,如果请求按时完成,则停止触发超时)。
    • @JamesHenstridge 是的,正确的方法是将 http 传输配置为自动超时。它将需要更少的代码和更简单的逻辑。见golang.org/pkg/net/http/#Transport
    【解决方案2】:

    如果您想避免将并发逻辑与业务逻辑混为一谈,我编写了这个库https://github.com/shomali11/parallelizer 来帮助您。它封装了并发逻辑,所以你不用担心。

    所以在你的例子中:

    package main
    
    import (
        "github.com/shomali11/parallelizer"
        "fmt"
    )
    
    func main() {
        urls := []string{ ... }
        results = make([]*HttpResponse, len(urls)
    
        options := &Options{ Timeout: time.Second }
        group := parallelizer.NewGroup(options)
        for index, url := range urls {
            group.Add(func(index int, url string, results *[]*HttpResponse) {
                return func () {
                    ...
    
                    results[index] = &HttpResponse{url, response, err}
                }
            }(index, url, &results))
        }
    
        err := group.Run()
    
        fmt.Println("Done")
        fmt.Println(fmt.Sprintf("Results: %v", results))
        fmt.Printf("Error: %v", err) // nil if it completed, err if timed out
    }
    

    【讨论】:

      猜你喜欢
      • 2012-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-05
      • 2010-12-12
      • 2014-04-25
      • 1970-01-01
      相关资源
      最近更新 更多