http.Server.ShutdownОбработка сигналов позволяет среагировать на завершение работы. Но она не ждёт текущие запросы. Чтобы это исправить, в Go есть
server.Shutdown().❔ Как это работает
Создаём
http.Server явно и используем его метод Shutdown:package main
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Println("handling request...")
time.Sleep(5 * time.Second)
fmt.Fprintln(w, "done")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
go func() {
fmt.Println("server started at :8080")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
panic(err)
}
}()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
<-ctx.Done()
fmt.Println("shutdown signal received")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
server.Shutdown(shutdownCtx)
fmt.Println("graceful shutdown complete")
}
Ключевой блок:
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
server.Shutdown(shutdownCtx)
server.Shutdown() останавливает приём новых соединений и ждёт, пока текущие запросы завершатся. Таймаут контекста страхует от зависания — если через 10 секунд что-то ещё не завершилось, процесс всё равно выйдет.🔍 Что остаётся нерешённым
Есть небольшое окно между получением сигнала и вызовом
Shutdown. В это время сервер всё ещё принимает новые соединения. При высокой нагрузке это может привести к непредсказуемому поведению — часть запросов получит ответ, часть будет оборвана.server.Shutdown() решает главную проблему — in-flight запросы дожидаются завершения. Для большинства сервисов этого достаточно. Но если нужна полная управляемость трафиком во время shutdown, нужен ещё один слой.📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoToProduction