简体   繁体   English

并发http请求没有响应

[英]concurrent http request gives no response

I am playing around with Go a bit and I've a problem that I am unable to solve. 我正在玩Go,我遇到了一个我无法解决的问题。

The following code is the least possible code that reproduces my problem. 以下代码是重现我的问题的最不可能的代码。 The goal of the original code is to delegate http request to goroutines. 原始代码的目标是将http请求委托给goroutines。 Each goroutine does a bit of heavy image calculations and is supposed to respond. 每个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")
}

In the case of a go delegate(w) I get no response, without the go it works out nicely. 在的情况下, go delegate(w)我没有得到任何回应,没有go它的作品了很好的。

Can anyone explain what's going on? 谁能解释一下发生了什么? Thanks a lot! 非常感谢!

ListenAndServe already launches goroutines to call your handler function, so you shouldn't do it yourself. ListenAndServe已经启动了goroutine来调用你的处理函数,所以你不应该自己动手。

Here's the code of 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)

So your code should simply be 所以你的代码应该就是这样

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")
}

The handler is already called from an "outer" goroutine (a per request one). 已经从“外部”goroutine(每个请求一个)调用处理程序。 The handler must do everything what has to be done, eg writing a full response, before it returns. 处理程序必须完成所有必须完成的工作,例如在返回之前编写完整的响应。 You're returning "prematurely" b/c of a superfluous go statement. 你正在“过早地”返回多余的go语句的b / c。 Please try simply to put the body of "delegate" in 'handle' and check if that improves something ;-) 请尝试简单地将“委托”的主体放在“句柄”中并检查是否有所改进;-)

Sometimes the Go scheduler can be really unmerciful to Goroutines. Go调度程序有时候对Goroutines来说真的无情。 The problem is this: you have an applications and you run go routines, so the scheduler thinks: hey, I might actually do some optimizations. 问题是这样的:你有一个应用程序,你运行例程,所以调度程序认为:嘿,我可能实际上做了一些优化。 Why don't I just run this certain Goroutine later to save some CPU time and make the application more responsive? 为什么我不运行这个特定的Goroutine以节省一些CPU时间并使应用程序更具响应性?

This is what happens: in your code is no way of enforcing the Goroutine to finish at some point. 这就是:在你的代码中没有办法强制Goroutine在某个时候完成。 In fact the Go documentation says this: 事实上,Go文档说明了这一点:

For example, in this program: 例如,在此程序中:

  var a string func hello() { go func() { a = "hello" }() print(a) } 

the assignment to a is not followed by any synchronization event, so it is not guaranteed to be observed by any other goroutine. 对a的赋值没有跟随任何同步事件,因此不保证任何其他goroutine都能观察到它。 In fact, an aggressive compiler might delete the entire go statement. 实际上,一个积极的编译器可能会删除整个go语句。

If the effects of a goroutine must be observed by another goroutine, use a synchronization mechanism such as a lock or channel communication to establish a relative ordering. 如果必须由另一个goroutine观察到goroutine的影响,请使用锁定或通道通信等同步机制来建立相对排序。

So the solution to your problem is to add a synchronisation event, eg by using a channel: 因此,您的问题的解决方案是添加同步事件,例如通过使用频道:

package main 包主

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
}

From: The Go Memory Model 来自: Go Memory Model

Anyways, as others have pointed out, your example is currently kind of artificial. 无论如何,正如其他人所指出的,你的例子目前是人为的。 But there are certainly scenarios in which it makes sense to call other Goroutines from inside an http handler. 但是有一些情况下,从http处理程序中调用其他Goroutines是有意义的。 Eg if you do heavy calculations and HTTP streaming at the same time, or several heavy calculations at the same time. 例如,如果您同时进行繁重的计算和HTTP流式传输,或同时进行多次繁重的计算。 Although I think in the latter case you would have probably added a channel yourself for synchronization purposes. 虽然我认为在后一种情况下你可能会自己添加一个频道用于同步目的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM