Go语言可以使用多线程来处理网络IO吗?

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

Can golang launch multi-thread to handle networkd IO

问题

我想开发一个使用Go语言处理来自多个TCP连接的请求的软件,并在具有10Gb-nic的服务器上运行。

似乎在单个核心上接收/发送数据的性能不够。因此,我希望实现软件在多个CPU核心上接收/发送数据。

然后,我创建了一个简单的测试服务器,以检查Go语言是否可以在多个CPU核心上接收/发送数据。它使用多个(16个)goroutine在同一个监听器上启动HTTP服务器,并使用ab(Apache Benchmark)作为客户端。

服务器启动后,我发现只有一个线程调用EpollWait,但服务器启动了18个线程,并且当我启动ab以使用16个并发进行测试时,服务器只占用一个核心。

所以问题是:是否有办法在Go语言中启动多个线程来处理来自多个TCP连接的数据接收/发送?或者我是否必须调用syscall.EpollWait来创建一个网络框架,自己来处理?

服务器的测试代码:

package main

import (
  "io"
  "log"
  "net"
  "net/http"
  "runtime"
)

type HandlerFunction struct{}

func (self HandlerFunction) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  data := "Hello"
  //fmt.Printf("data_len=%d\n", len(data))
  io.WriteString(w, string(data))
}

func RoutineFunction(hs *http.Server, l net.Listener) {
  runtime.LockOSThread()
  err := hs.Serve(l)
  if err != nil {
    log.Fatalf("serve fail, err=[%s]", err)
  }
}

func main() {
  runtime.GOMAXPROCS(16)

  l, err := net.Listen("tcp", "0.0.0.0:12345")
  if err != nil {
    log.Fatalf("listen fail, err=[%s]", err)
  }

  for i := 0; i < 15; i++ {
    hs := http.Server{}
    hs.Handler = HandlerFunction{}
    go RoutineFunction(&hs, l)
  }

  hs := http.Server{}
  hs.Handler = HandlerFunction{}
  RoutineFunction(&hs, l)
}
英文:

I want to develop a software to handle request from multiple tcp connections using GoLang, and run on the server with 10Gb-nic.

It seems that the performance is not enough to recv/send data on the single core. So I want to implement the software to recv/send data on multiple cpu cores.

Then I made a simple test server to check whether GoLang can recv/send data on multiple cpu cores or not. It launch multiple(16) goroutines to start http server on the same listener, and use ab(Apache Benchmark) as client.

After the server start, I have seen only one thread invoke EpollWait,but the server launched 18 threads, and when I start ab to test using 16 concurrency, but the server occupy only one core.

So the question: is there any way to launch multiple threads to handle data recv/send from multiple tcp connections in GoLang. Or Should I have to invoke syscall.EpollWait to make a Network Framework, to do it myself?

The server's test code:

package main

import (
  &quot;io&quot;
  &quot;log&quot;
  &quot;net&quot;
  &quot;net/http&quot;
  &quot;runtime&quot;
)

type HandlerFunction struct{}

func (self HandlerFunction) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  data := &quot;Hello&quot;
  //fmt.Printf(&quot;data_len=%d\n&quot;, len(data))
  io.WriteString(w, string(data))
}

func RoutineFunction(hs *http.Server, l net.Listener) {
  runtime.LockOSThread()
  err := hs.Serve(l)
  if err != nil {
    log.Fatalf(&quot;serve fail, err=[%s]&quot;, err)
  }
}

func main() {
  runtime.GOMAXPROCS(16)

  l, err := net.Listen(&quot;tcp&quot;, &quot;0.0.0.0:12345&quot;)
  if err != nil {
    log.Fatalf(&quot;listen fail, err=[%s]&quot;, err)
  }

  for i := 0; i &lt; 15; i++ {
    hs := http.Server{}
    hs.Handler = HandlerFunction{}
    go RoutineFunction(&amp;hs, l)
  }

  hs := http.Server{}
  hs.Handler = HandlerFunction{}
  RoutineFunction(&amp;hs, l)
}

答案1

得分: 6

并不完全准确。

Go运行时(截至go1.5版本)使用单个网络轮询器。当服务器有实际工作需要完成时,这很少是瓶颈,并且运行goroutine的线程将保持忙碌。然而,在某些情况下,如果核心数足够多或吞吐量足够大,Go运行时可能会开始受到影响,特别是因为轮询器通常位于与执行IO的线程不同的NUMA节点上。

如果您需要在这个规模上运行,我目前建议将Go服务器限制在单个NUMA节点上,并运行多个服务器实例。

唯一的例外是,如果将套接字设置为阻塞模式,那么该套接字上的IO将绑定到单个操作系统线程。我还没有对这种方法进行过吞吐量测试,以查看是否有任何好处,但如果您同时使用的套接字相对较少,尝试一下也无妨。

英文:

Not exactly.

The Go runtime (as of go1.5) uses a single network poller. When you have actual work do be done in the server, this is rarely the bottleneck, and the threads running goroutines will be kept busy. In some cases though, either with enough cores, or enough throughput, the Go runtime will start to suffer, especially since the poller will often be in a different NUMA node than the thread doing the IO.

If you need to run at that scale, I current suggest limiting the Go server to a single NUMA node, and running multiple instances of the server.

The exception to this is that if you put the socket into blocking mode, then IO on that socket will be bound to a single OS thread. I haven't done any throughput tests on this method to see if there's any benefit, but if you're using relatively few sockets concurrently, it couldn't hurt to try.

huangapple
  • 本文由 发表于 2015年9月4日 00:10:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/32380653.html
匿名

发表评论

匿名网友

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

确定