2012-06-10 17 views
19

mam postawić serwer HTTP napisany w Go i robi się trochę ponad tysiąc gości dziennie. Mam teraz narastający problem Goroutine. W ciągu dnia wydaje mi się, że z serwera http dostaje się nieco ponad tysiąc nowych Goroutines.Golang: http serwer pozostawiając otwarte goroutines

Nie jestem pewien, jak mogłem bałagan obsługi.

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

Poniżej jest jednym z goroutines ze stosu

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 

Wydaje się, że połączenia są utknięcie w stanie odczytu. Podobnie jak serwer http nie ma na nie czasu. Czy domyślny serwer nie ma czasu oczekiwania na odczyt?

wersja przejdź GO1

Odpowiedz

54

Powodem wszystkie te goroutines czytasz to keep-alive. Gdy przeglądarka wysyła nagłówek keep-alive, serwer utrzymuje połączenie otwarte, aby zaakceptować więcej żądań. Jest to dobre rozwiązanie, gdy klient żąda wielu małych plików, a połączenie TCP ma znaczny narzut. Czas oczekiwania na odczyt zapewni, że żadne połączenie nie będzie utrzymywane przy życiu dłużej niż pewien określony czas między żądaniami. Pozwoli to zamknąć połączenia, ale także uniemożliwi przesyłanie danych przez dłuższy czas niż limit czasu. Niestety, nie ma jeszcze opcji konkretnego limitu czasu podtrzymywania aktywności.

Domyślnie nie ma limitu czasu. Można ustawić limit czasu w struktury Server http://golang.org/pkg/net/http/#Server

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

Dzięki! Spróbuję tej aktualizacji w tym tygodniu. :-) – Daniel

+1

Tak, to naprawiło. Dzięki! – Daniel

+0

Czy możesz oznaczyć to jako odebrane? Dzięki –

0

Bądź ostrożny używając ReadTimeout i WriteTimeout na połączeniach HTTP. Nie sądzę, żeby robili to, czego się od nich oczekuje. W szczególności mają tendencję do pozostawienia połączeń w stanie bezużytecznym bez ich zamykania. Szczegółowe informacje można znaleźć na stronie https://groups.google.com/forum/#!topic/golang-nuts/oBIh_R7-pJQ, w szczególności widzę przypadki, w których ReadTimeout sprawia, że ​​połączenia stają się bezużyteczne i powoduje, że odpowiedzi są usuwane na podłogę, gdy czas obsługi HTTP przekroczy limit czasu. Jeśli ustawisz timeout na dużą wartość, która przekracza czas odpowiedzi dowolnego programu obsługi, możesz być w porządku.

0

Szczepana odpowiedź pracował dla mnie tylko w połączeniu z: serwer może powiedzieć swoim klientom, że nie chcą lub wsparcia, aby utrzymać połączenia otwarte. Aby to zrobić, należy ustawić flagę zgodnie przed podaniem:

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

Spowoduje to ustawienie nagłówka odpowiedzi Connection: close i większość klientów zakończy połączenie z ich strony.