Is creating a goroutine per request (e.g., in net/http) a good design? What are the risks?
Question 176MediumGo 1.22 to 1.25
Yes. net/http runs one goroutine per connection, and each request on that connection is handled in that goroutine (HTTP/2 uses one goroutine per stream). This works because goroutines are cheap and the netpoller parks idle connections without holding threads. The code stays sequential and easy to read.
Risks to discuss:
- No built-in backpressure. A traffic spike creates unlimited goroutines and memory use. Mitigate with
ReadTimeout,WriteTimeoutandIdleTimeout,MaxHeaderBytes,netutil.LimitListener, load shedding, or a concurrency-limiting middleware. - Goroutines started from handlers outlive the request unless they are tied to
r.Context(). For fire-and-forget work, usecontext.WithoutCancelplus your own timeout, and track the goroutines so graceful shutdown can wait for them. - Panics in those child goroutines crash the server. Only the handler goroutine itself is protected.
- Slowloris-style attacks keep many goroutines alive when timeouts are missing.
srv := &http.Server{
Addr: ":8080",
ReadHeaderTimeout: 5 * time.Second,
IdleTimeout: 60 * time.Second,
Handler: h,
}
What the interviewer wants to hear: goroutine-per-connection is a good default, as long as you add timeouts, limits and lifecycle management.
More on Goroutines & the Scheduler
- Q174How do goroutines interact with cgo, and why can cgo calls be expensive?
- Q175Implement a bounded, cancellable fan-out that returns the first error and does not leak goroutines.
- Q177How are timers handled by the scheduler, and what changed with time.Timer in Go 1.23?
- Q178What are the scheduling implications of runtime.Goexit, blocking in init, and unbuffered sends from many goroutines?
- Q179When are the arguments of a go statement evaluated? What does this print?
- Q180What does the Go memory model guarantee about starting and ending a goroutine?