Подтвердить что ты не робот

Golang: http-сервер, оставляющий открытые goroutines

Я поставил http-сервер, написанный на Go, и он получает чуть более тысячи посетителей в день. Сейчас у меня накопительная проблема Горутина. В течение дня я, кажется, получаю чуть более тысячи новых Goroutines с http-сервера.

Я не уверен, как я могу испортить обработчик.

http.Handle("/", http.FileServer(http.Dir(config.htdocs_path)))

Ниже приведен один из goroutines из стека

goroutine 1582 [chan receive]:
net.(*pollServer).WaitRead(0xf84007f680, 0xf84066dea0, 0xf84007aa80, 0xb, 0x1, ...)
        /home/ec2-user/go/src/pkg/net/fd.go:268 +0x73
net.(*netFD).Read(0xf84066dea0, 0xf840ec1000, 0x100000001000, 0x7f7effffffff, 0xf84007c0f0, ...)
        /home/ec2-user/go/src/pkg/net/fd.go:428 +0x1ec
net.(*TCPConn).Read(0xf84068aff8, 0xf840ec1000, 0x100000001000, 0xf800000002, 0x0, ...)
        /home/ec2-user/go/src/pkg/net/tcpsock_posix.go:87 +0xce
io.(*LimitedReader).Read(0xf840d1bc20, 0xf840ec1000, 0x100000001000, 0xdcb00000000, 0x0, ...)
        /home/ec2-user/go/src/pkg/io/io.go:394 +0xc1
bufio.(*Reader).fill(0xf8405b0900, 0xdcb00000000)
        /home/ec2-user/go/src/pkg/bufio/bufio.go:77 +0xf0
bufio.(*Reader).ReadSlice(0xf8405b0900, 0xf840d1bc0a, 0x0, 0x0, 0x0, ...)
        /home/ec2-user/go/src/pkg/bufio/bufio.go:257 +0x1b6
bufio.(*Reader).ReadLine(0xf8405b0900, 0x0, 0x0, 0x0, 0x0, ...)
        /home/ec2-user/go/src/pkg/bufio/bufio.go:283 +0x5b
net/textproto.(*Reader).readLineSlice(0xf840730660, 0xc0, 0x100000000, 0x7f7e00000001)
        /home/ec2-user/go/src/pkg/net/textproto/reader.go:55 +0x4f
net/textproto.(*Reader).ReadLine(0xf840730660, 0xf84061f300, 0x0, 0x48411c)
        /home/ec2-user/go/src/pkg/net/textproto/reader.go:36 +0x25
net/http.ReadRequest(0xf8405b0900, 0xf84061f300, 0x0, 0x0, 0x100000400ccf60, ...)
        /home/ec2-user/go/src/pkg/net/http/request.go:457 +0xb1
net/http.(*conn).readRequest(0xf8402b2b40, 0xf8400e3fc0, 0x0, 0x0, 0xf8405b0a80, ...)
        /home/ec2-user/go/src/pkg/net/http/server.go:240 +0xa8
net/http.(*conn).serve(0xf8402b2b40, 0x0)
        /home/ec2-user/go/src/pkg/net/http/server.go:594 +0x145
created by net/http.(*Server).Serve
        /home/ec2-user/go/src/pkg/net/http/server.go:1040 +0x430

Кажется, что соединения застревают в состоянии чтения. Как и http-сервер, они не синхронизируют их. У сервера по умолчанию нет тайм-аута чтения?

go версия go1

4b9b3361

Ответ 1

Причина, по которой все эти горуты читают, остается живой. Когда браузер отправляет заголовок keep-alive, сервер держит соединение открытым, чтобы принимать больше запросов. Это хорошо, когда клиент запрашивает много мелких файлов, а TCP-соединение - значительные накладные расходы. Тайм-аут чтения гарантирует, что соединение не будет поддерживаться более определенного времени между запросами. Это закрыло бы тесные связи, но также не позволяло кому-то загружаться дольше, чем таймаут. К сожалению, пока еще нет специального параметра тайм-аута сохранения.

По умолчанию нет таймаута. Вы можете установить тайм-аут в структуре сервера http://golang.org/pkg/net/http/#Server

srv := &http.Server{
    Handler: http.FileServer(http.Dir(config.htdocs_path)),
    ReadTimeout: 30*time.Second,
}
srv.ListenAndServe()

Ответ 2

Будьте внимательны при использовании ReadTimeout и WriteTimeout в HTTP-соединениях. Я не думаю, что они делают то, что вы ожидаете от них. В частности, они имеют тенденцию оставлять связи в непригодном для использования состоянии, фактически не закрывая их. Подробнее см. https://groups.google.com/forum/#!topic/golang-nuts/oBIh_R7-pJQ, в частности, я вижу случаи, когда ReadTimeout делает подключения непригодными и вызывает отбрасывание ответов на пол, когда HTTP время обработки превышает таймаут. Если вы установите тайм-аут на большое значение, превышающее время отклика любого обработчика, вы можете быть в порядке.

Ответ 3

Ответ Стивена работал у меня только в сочетании со следующим: сервер может сообщать своим клиентам, что он не хочет или не поддерживает открытие соединений. Для этого перед подачей установите соответствующий флаг:

server := &http.Server{
  // ... see Stephen answer
}
server.SetKeepAlivesEnabled(false)
server.ListenAndServe()

Это установит заголовок ответа Connection: close, и большинство клиентов прекратит соединение с их стороны.