Goroutines with ListenAndServe能提高性能吗?

huangapple go评论95阅读模式
英文:

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 (
	&quot;fmt&quot;
	&quot;net/http&quot;
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, &quot;Hello OpenShift!&quot;)
}

func main() {
	http.HandleFunc(&quot;/&quot;, helloHandler)

	go func() {
		fmt.Println(&quot;serving on 8080&quot;)
		err := http.ListenAndServe(&quot;:8080&quot;, nil)
		if err != nil {
			panic(&quot;ListenAndServe: &quot; + err.Error())
		}
	}()

	go func() {
		fmt.Println(&quot;serving on 8888&quot;)
		err := http.ListenAndServe(&quot;:8888&quot;, nil)
		if err != nil {
			panic(&quot;ListenAndServe: &quot; + 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(&quot;/&quot;, responsehandler.Handler)
    go func() {
      http.ListenAndServe(&quot;:8888&quot;, 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(&quot;localhost:6060&quot;, 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 _ &quot;profiling&quot; 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

huangapple
  • 本文由 发表于 2015年5月28日 00:05:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/30487703.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定