【发布时间】:2015-10-11 08:30:51
【问题描述】:
我需要编写一个可以处理超过 500k 连接的 tcp 服务器。
我在golang上写了一个简单的服务器,但是当连接数超过10k时,服务器崩溃了,报错“runtime: program beyond 10000-thread limit fatal error: thread exhaustion”。
服务器在最后一个 linux 版本中运行。那 go 可以使用 epoll 进行 tcp 连接,使其异步并使用少量 fd。那么为什么服务器会超出线程限制呢?!
我的简单服务器:
package main
import (
"strconv"
"net"
"log"
"time"
"bufio"
"io"
)
type Handler struct {
conn net.Conn
closed chan bool
}
func (h *Handler) Listen() { // listen connection for incomming data
defer h.conn.Close()
bf := bufio.NewReader(h.conn)
for {
line, _, err := bf.ReadLine()
if err != nil {
if err == io.EOF {
log.Println("End connection")
}
h.closed <- true // send to dispatcher, that connection is closed
return
}
// ... some business logic with data
}
}
type Dispatcher struct {
handlers map[string]*Handler `map[ip]*Handler`
}
func (d *Dispatcher) AddHandler(conn net.Conn) {
addr := conn.RemoteAddr().String()
handler := &Handler{conn, make(chan bool, 1)}
d.handlers[addr] = handler
go handler.Listen()
<-handler.closed // when connection closed, remove handler from handlers
delete(d.handlers, addr)
}
func (d *Dispatcher) ListenHandlers(port int) {
sport := strconv.Itoa(port)
ln, err := net.Listen("tcp", ":" + sport)
if err != nil {
log.Println(err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept() // accept connection
if err != nil {
log.Println(err)
continue
}
tcpconn := conn.(*net.TCPConn)
tcpconn.SetKeepAlive(true)
tcpconn.SetKeepAlivePeriod(10 * time.Second)
go d.AddHandler(conn)
}
}
func main() {
dispatcher := &Dispatcher{make(map[string]*Handler)}
dispatcher.ListenHandlers(3000)
}
11.10.2015 更新
问题是https://github.com/felixge/tcpkeepalive 库。请不要使用它:)
【问题讨论】:
-
每当一个 goroutine 进行阻塞系统调用或去调用库中的 C 代码时,go 都会为其他 goroutine 使用一个新线程。我怀疑注释为
// ... some business logic with data的部分有一些阻塞导致 go 旋转额外的 goroutine。您是否有机会在那部分调用一些 C 代码? -
@sintanial:Go 的
net包将使用 epoll/select 对多个套接字进行多路读取和写入。您是否尝试过使用 gdb 检查应用程序以查看看到此错误时所有线程在做什么? -
TCP 连接在每个对等点上使用一个端口。端口由 16 位数字表示,因此您对单个 IP 地址上可以拥有的 TCP 连接数有技术限制,这与 0.5M 连接的目标相去甚远。你想做什么?
-
@UlrichEckhardt 来自多个源地址和端口的 AFAIK,您可以拥有超过 65k 个连接,因为每个连接都有两个由它标识的地址。
-
@Not_a_Golfer 你是对的。我删除了我的业务逻辑,并运行服务器,它工作正常。在 34 个线程中运行,仅此而已,线程没有增加:)。谢谢你,我认为在golang问题中:)
标签: multithreading tcp go