首页 文章

并发http请求没有响应

提问于
浏览
8

我正在玩Go,我遇到了一个我无法解决的问题 .

以下代码是重现我的问题的最不可能的代码 . 原始代码的目标是将http请求委托给goroutines . 每个goroutine做了一些沉重的图像计算,并应该响应 .

package main

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

func main() {
    http.HandleFunc("/", handle)
    http.ListenAndServe(":8080", nil)
}

func handle(w http.ResponseWriter, r *http.Request) {

    // the idea is to be able to handle several requests
    // in parallel

    // the "go" is problematic
    go delegate(w)
}

func delegate(w http.ResponseWriter) {

    // do some heavy calculations first

    // present the result (in the original code, the image)
    fmt.Fprint(w, "hello")
}

go delegate(w) 的情况下,我没有得到回应,没有 go 它很好地解决了 .

谁能解释一下发生了什么?非常感谢!

3 回答

  • 1

    ListenAndServe 已经启动goroutine来调用你的处理函数,所以你不应该自己动手 .

    这是the relevant functions from the package source的代码:

    1089    func ListenAndServe(addr string, handler Handler) error {
    1090        server := &Server{Addr: addr, Handler: handler}
    1091        return server.ListenAndServe()
    1092    }
    
    
    1010    func (srv *Server) ListenAndServe() error {
    1011        addr := srv.Addr
    1012        if addr == "" {
    1013            addr = ":http"
    1014        }
    1015        l, e := net.Listen("tcp", addr)
    1016        if e != nil {
    1017            return e
    1018        }
    1019        return srv.Serve(l)
    1020    }
    
    
    1025    func (srv *Server) Serve(l net.Listener) error {
    1026        defer l.Close()
    1027        var tempDelay time.Duration // how long to sleep on accept failure
    1028        for {
    
    1057            go c.serve()
    1058        }
    1059        panic("not reached")
    1060    }
    
    
    579 // Serve a new connection.
    580 func (c *conn) serve() {
    581     defer func() {
    582         err := recover()
    
    669         handler.ServeHTTP(w, w.req)
    

    所以你的代码应该就是这样

    func handle(w http.ResponseWriter, r *http.Request) {
        // the idea is to be able to handle several requests
        // in parallel
        // do some heavy calculations first
    
        // present the result (in the original code, the image)
        fmt.Fprint(w, "hello")
    }
    
  • 5

    处理程序已经从“外部”goroutine(每个请求一个)调用 . 处理程序必须完成所有必须完成的工作,例如在它返回之前写出完整的回复 . 你正在“过早地”返回多余的go语句的b / c . 请尝试简单地将“委托”的主体放在“句柄”中并检查是否有所改进;-)

  • 1

    有时,Go调度程序对Goroutines来说真的无法实现 . 问题是:你有一个应用程序,你运行例程,所以调度程序认为:嘿,我可能实际上做了一些优化 . 为什么我不能稍后运行这个特定的Goroutine以节省一些CPU时间并使应用程序更具响应性?

    这就是发生的事情:在你的代码中没有办法强制Goroutine在某些时候完成 . 事实上,Go文档说明了这一点:

    例如,在此程序中:var a string

    func hello(){
    go func()
    ()
    打印(一)
    }
    对a的赋值没有跟随任何同步事件,因此不保证任何其他goroutine都能观察到它 . 实际上,一个积极的编译器可能会删除整个go语句 . 如果必须由另一个goroutine观察到goroutine的影响,请使用锁定或通道通信等同步机制来 Build 相对排序 .

    因此,您的问题的解决方案是添加同步事件,例如使用 Channels :

    包主

    import (
      "fmt"
      "net/http"
    )
    
    func main() {
        http.HandleFunc("/", handle)
        http.ListenAndServe(":8080", nil)
    }
    
    func handle(w http.ResponseWriter, r *http.Request) {
        // the idea is to be able to handle several requests
        // in parallel
    
        // the "go" is problematic...
        ch := make(chan int)
        go delegate(w, ch)
        // ...but not anymore:
        <-ch
    }
    
    func delegate(w http.ResponseWriter, ch chan<- int) {
        // do some heavy calculations first
        // present the result (in the original code, the image)
        fmt.Fprint(w, "hello")
        ch <- 1
    }
    

    来自:The Go Memory Model

    无论如何,正如其他人所指出的那样,你的例子目前是人为的 . 但是有一些情况下,从http处理程序中调用其他Goroutines是有意义的 . 例如 . 如果你同时进行繁重的计算和HTTP流式传输,或者同时进行几次繁重的计算 . 虽然我认为在后一种情况下你可能会自己添加一个 Channels 用于同步目的 .

相关问题