当前位置: 首页 > 面试题库 >

golang http超时和goroutines累积

逄皓轩
2023-03-14
问题内容

我使用goroutines实现http.Get超时,然后我发现goroutines的数量一直在稳定增长,当达到1000左右时,程序将退出

码:

package main

import (
        "errors"
        "io/ioutil"
        "log"
        "net"
        "net/http"
        "runtime"
        "time"
)

// timeout dialler
func timeoutDialler(timeout time.Duration) func(network, addr string) (net.Conn, error) {
        return func(network, addr string) (net.Conn, error) {
                return net.DialTimeout(network, addr, timeout)
        }
}

func timeoutHttpGet(url string) ([]byte, error) {
        // change dialler add timeout support && disable keep-alive
        tr := &http.Transport{
                Dial:              timeoutDialler(3 * time.Second),
                DisableKeepAlives: true,
        }

        client := &http.Client{Transport: tr}

        type Response struct {
                resp []byte
                err  error
        }

        ch := make(chan Response, 0)
        defer func() {
                close(ch)
                ch = nil
        }()

        go func() {
                resp, err := client.Get(url)
                if err != nil {
                        ch <- Response{[]byte{}, err}
                        return
                }
                defer resp.Body.Close()

                body, err := ioutil.ReadAll(resp.Body)
                if err != nil {
                        ch <- Response{[]byte{}, err}
                        return
                }

                tr.CloseIdleConnections()
                ch <- Response{body, err}
        }()

        select {
        case <-time.After(5 * time.Second):
                return []byte{}, errors.New("timeout")
        case response := <-ch:
                return response.resp, response.err
        }
}

func handler(w http.ResponseWriter, r *http.Request) {
        _, err := timeoutHttpGet("http://google.com")
        if err != nil {
                log.Println(err)
                return
        }
}

func main() {
        go func() {
                for {
                        log.Println(runtime.NumGoroutine())
                        time.Sleep(500 * time.Millisecond)
                }
        }()

        s := &http.Server{
                Addr:         ":8888",
                ReadTimeout:  15 * time.Second,
                WriteTimeout: 15 * time.Second,
        }

        http.HandleFunc("/", handler)
        log.Fatal(s.ListenAndServe())
}

http://play.golang.org/p/SzGTMMmZkI


问题答案:

用1而不是0初始化chan:

ch := make(chan Response, 1)

并删除关闭并延迟ch的延迟块。

参见:http :
//blog.golang.org/go-concurrency-patterns-timing-out-
and

我认为这是正在发生的事情:

  1. 5s超时后,timeoutHttpGet返回
  2. defer语句运行,关闭ch,然后将其设置为nil
  3. go例程开始执行实际的提取操作,然后尝试将其数据发送到ch
  4. 但ch为零,因此不会收到任何东西,从而阻止了该语句的完成,从而阻止了go例程的完成

我假设您正在设置,ch = nil是因为在您这样做之前,您会遇到运行时恐慌,因为这是您尝试写入规范中所述的关闭通道时发生的情况。

给ch一个1的缓冲区意味着fetch go例程可以发送给它而无需接收者。如果处理程序由于超时而返回,则稍后将收集所有垃圾。



 类似资料:
  • 9.8. Goroutines和线程 在上一章中我们说goroutine和操作系统的线程区别可以先忽略。尽管两者的区别实际上只是一个量的区别,但量变会引起质变的道理同样适用于goroutine和线程。现在正是我们来区分开两者的最佳时机。 9.8.1. 动态栈 每一个OS线程都有一个固定大小的内存块(一般会是2MB)来做栈,这个栈会用来存储当前正在被调用或挂起(指在调用其它函数时)的函数的内部变量。

  • 8.1. Goroutines 在Go语言中,每一个并发的执行单元叫作一个goroutine。设想这里的一个程序有两个函数,一个函数做计算,另一个输出结果,假设两个函数没有相互之间的调用关系。一个线性的程序会先调用其中的一个函数,然后再调用另一个。如果程序中包含多个goroutine,对两个函数的调用则可能发生在同一时刻。马上就会看到这样的一个程序。 如果你使用过操作系统或者其它语言提供的线程,那

  • 并发程序指同时进行多个任务的程序,随着硬件的发展,并发程序变得越来越重要。Web服务器会一次处理成千上万的请求。平板电脑和手机app在渲染用户画面同时还会后台执行各种计算任务和网络请求。即使是传统的批处理问题--读取数据,计算,写输出--现在也会用并发来隐藏掉I/O的操作延迟以充分利用现代计算机设备的多个核心。计算机的性能每年都在以非线性的速度增长。 Go语言中的并发程序可以用两种手段来实现。本章

  • 8.7. Goroutines Go语言中使用go可以启动一个goroutine。goroutine 和线程的概念类似,和程序共享一个地址空间。 goroutines和支持多路并发草组系统中的协程(coroutines)类似,用户不用关心具体 的实现细节。 func server(i int) { for { print(i) sys.sle

  • 问题内容: 我试图了解Go中的并发性。特别是,我编写了以下线程不安全程序: 我认识到我应该使用渠道来防止与发生竞争,但这不是重点。程序打印,然后似乎永远循环(不再打印任何内容)。我希望它能打印出无限的数字列表,可能由于竞态条件而跳过某些数字并重复其他数字(或更糟的是,在更新数字时打印数字)。 我的问题是:为什么程序只打印一行? 只是要清楚一点:对于这个玩具示例,我不是故意使用渠道。 问题答案: 关

  • 问题内容: 我想对列进行累加,但是每当遇到0时就重置聚合值 这是我尝试做的一个例子: 该数据集: 给出以下内容: 问题答案: 在SQL Server 2008中,由于无法使用分析函数,因此受到了严重限制。以下方法效率不高,但可以解决您的问题: las,在SQL Server 2012之前,最有效的解决方案可能涉及游标。在SQL Server 2012+中,您只需执行以下操作: