英文:
Goroutines with ListenAndServe increases performance?
问题
我对Go语言的协程不太熟悉,但是在使用net/http
的路由器时,我多次看到ListenAndServe()
被一个协程包装起来。
一个服务器需要能够同时处理多个请求,以提高效率。那么为什么要使用协程作为"轻量级线程"呢?并发性有什么优势吗?
以下是一个由OpenShift提供的示例代码:
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello OpenShift!")
}
func main() {
http.HandleFunc("/", helloHandler)
go func() {
fmt.Println("serving on 8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}()
go func() {
fmt.Println("serving on 8888")
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}()
select {}
}
希望对你有帮助!
英文:
I'm not very familiar with Go's routines but since I'm working with the router of net/http
I saw a few times thatListenAndServe()
is wrapped by a go routine.
A server needs to be able to handle multiple requests simultaneously out of the box to be efficient. So why are go routines as 'lightweight threads' used?
Does the concurrency provide any advantages?
Here's an example by OpenShift
<!-- language: go-lang -->
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello OpenShift!")
}
func main() {
http.HandleFunc("/", helloHandler)
go func() {
fmt.Println("serving on 8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}()
go func() {
fmt.Println("serving on 8888")
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}()
select {}
}
答案1
得分: 25
http.ListenAndServe
是一个阻塞调用。如果你想继续做更多的工作(比如进行第二个 http.ListenAndServe
调用),你需要将它移到一个单独的 goroutine 中。这就是他们在这里所做的。
他们在最后使用 select{}
来阻塞主 goroutine,因为他们所有对 http.ListenAndServe
的调用都在其他 goroutine 中。如果他们没有调用 select{}
,程序会终止,因为 main()
会返回。
他们也可以通过去掉 select{}
,并移除最后一段代码周围的 go func()
包装来实现相同的效果。但我猜他们这样做是为了让所有的代码保持一致。
但这与性能无关。
在你提供的评论中,还有其他类似的例子。在第一个例子中:
func main() {
http.HandleFunc("/", responsehandler.Handler)
go func() {
http.ListenAndServe(":8888", nil)
}()
fileservice.NewWatcher()
}
这里调用了 http.ListenAndServe
,然后调用了 fileservice.NewWatcher()
(它会阻塞)。如果他们没有将调用包装在一个 goroutine 中,fileservice.NewWatcher()
就永远不会被调用。
func init() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
}
这会启动调试分析器的 Web 服务器。同样,它是一个 goroutine,这样调用 init
就会立即返回而不是阻塞。这种特殊情况允许调用者只需 import _ "profiling"
,就可以“神奇地”获得调试分析器的 Web 服务器。
英文:
http.ListenAndServe
is a blocking call. If you want to go one doing more work (like making a second http.ListenAndServe
call), you need to move it over to a separate goroutine. That's all they're doing here.
They're using select{}
at the end to block the main goroutine, since all their calls to http.ListenAndServe
are on other goroutines. If they didn't call select{}
, the program would terminate because main()
would return.
They could have achieved the same thing by dropping select{}
, and removing the go func()
wrapper around the last block of code. But I suspect they did it this way so that all the code is consistent.
But this has nothing to do with performance.
In the comments you provided some other examples that are similar. In the first example:
func main() {
http.HandleFunc("/", responsehandler.Handler)
go func() {
http.ListenAndServe(":8888", nil)
}()
fileservice.NewWatcher()
}
This calls http.ListenAndServe
and then calls fileservice.NewWatcher()
(which blocks). If they hadn't wrapped the call in a goroutine, fileservice.NewWatcher()
would never have been called.
The other two examples are a common piece of boilerplate:
func init() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
}
This turns on the debug profiler web server. Again, it's a goroutine so that calling init
returns immediately rather than blocking. This particular case allows the caller to just import _ "profiling"
and "magically" get the debug profiler web server.
答案2
得分: 1
除了“在后台运行”之外,它没有任何特殊的好处。
英文:
No it does not have any special benefits beside being "run in the background".
答案3
得分: 0
我认为你不需要使用go routine来启动ListenAndServe。根据Go文档的说明:
"ListenAndServe调用Serve函数"。Serve函数是一个go routine。
ListenAndServe监听TCP网络地址addr,并调用Serve函数来处理传入连接上的请求。接受的连接被配置为启用TCP keep-alives。handler通常为nil,此时会使用DefaultServeMux。
https://golang.org/pkg/net/http/#ListenAndServe
func Serve(l net.Listener, handler Handler) error
Serve函数在监听器l上接受传入的HTTP连接,为每个连接创建一个新的服务goroutine。服务goroutine读取请求,然后调用handler函数进行回复。如果handler为nil,则使用DefaultServeMux。
https://golang.org/pkg/net/http/#Serve
英文:
I don't think you need a go routine to start ListenAndServe. According to the Go Documentations.
"ListenAndServe calls Serve". Serve is go routine.
ListenAndServe listens on the TCP network address addr and then calls Serve with handler to handle requests on incoming connections. Accepted connections are configured to enable TCP keep-alives. Handler is typically nil, in which case the DefaultServeMux is used.
https://golang.org/pkg/net/http/#ListenAndServe
func Serve(l net.Listener, handler Handler) error
Serve accepts incoming HTTP connections on the listener l, creating a new service goroutine for each. The service goroutines read requests and then call handler to reply to them. Handler is typically nil, in which case the DefaultServeMux is used.
https://golang.org/pkg/net/http/#Serve
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论