Как реализовать graceful shutdown в Fiber?
Graceful shutdown в Fiber реализуется через os/signal: перехватываем SIGTERM/SIGINT, вызываем app.ShutdownWithContext(ctx) с дедлайном, чтобы дать обработчикам время завершить текущие запросы перед остановкой.
Зачем нужен graceful shutdown
При получении SIGTERM (Kubernetes pod eviction, rolling deploy) сервер должен: прекратить принимать новые соединения, дать текущим запросам завершиться (обычно 15–30 секунд), закрыть пул БД и другие ресурсы. Без этого — обрезанные HTTP-ответы и потерянные транзакции.
Базовая реализация
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New(fiber.Config{
IdleTimeout: 5 * time.Second,
})
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("hello")
})
// Запускаем сервер в горутине
go func() {
if err := app.Listen(":3000"); err != nil {
log.Printf("server stopped: %v", err)
}
}()
// Ждём SIGINT или SIGTERM
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("shutting down server...")
// Даём 30 секунд на завершение текущих запросов
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := app.ShutdownWithContext(ctx); err != nil {
log.Printf("shutdown error: %v", err)
}
log.Println("server exited")
}
Graceful shutdown с cleanup ресурсов
func main() {
dbPool := setupDB()
redisClient := setupRedis()
app := fiber.New()
registerRoutes(app, dbPool, redisClient)
go func() {
if err := app.Listen(":3000"); err != nil {
log.Println(err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 1. Остановить приём новых соединений
if err := app.ShutdownWithContext(ctx); err != nil {
log.Printf("http shutdown: %v", err)
}
// 2. Закрыть ресурсы после остановки сервера
dbPool.Close()
if err := redisClient.Close(); err != nil {
log.Printf("redis close: %v", err)
}
log.Println("cleanup done")
}
Использование app.Shutdown() (без context)
Метод app.Shutdown() ждёт завершения всех активных соединений без таймаута. В продакшене предпочтительнее ShutdownWithContext с разумным дедлайном — иначе зависший хендлер заблокирует завершение процесса навсегда.
Kubernetes: terminationGracePeriodSeconds
Согласуйте таймаут shutdown с terminationGracePeriodSeconds в Pod spec. Обычная схема:
spec:
terminationGracePeriodSeconds: 60 # k8s даёт 60 сек
containers:
- name: api
lifecycle:
preStop:
exec:
command: ["/bin/sleep", "5"] # ждём снятия из endpoints
Внутри приложения таймаут ShutdownWithContext должен быть меньше terminationGracePeriodSeconds (например, 45 секунд).
Подводные камни
- Закрытие ресурсов до Shutdown: если закрыть БД-пул до
app.Shutdown(), хендлеры активных запросов получат ошибки соединения. - Нет таймаута на Shutdown:
app.Shutdown()без context блокируется навсегда при зависших WebSocket-соединениях. - SIGKILL не перехватывается:
signal.Notifyне ловит SIGKILL; Kubernetes посылает SIGKILL послеterminationGracePeriodSeconds— убедитесь, что cleanup укладывается в дедлайн. - Prefork и shutdown: в prefork-режиме
app.Shutdown()должен вызываться в каждом дочернем процессе; мастер-процесс Fiber делает это автоматически, но кастомный cleanup вfiber.IsChild()нужен явно. - Горутины с незавершёнными задачами: Shutdown закрывает HTTP-слой, но фоновые goroutine продолжают работать; используйте
sync.WaitGroupдля их ожидания. - Отсутствие preStop hook: без preStop в Kubernetes запросы могут приходить на pod, который уже начал завершение — добавьте sleep 5 в preStop.
Common mistakes
- Давать ответ про graceful shutdown в Fiber только на уровне определения, не показывая поведение в реальном приложении.
- Игнорировать границы ответственности вокруг темы «graceful shutdown в Fiber»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
- Не связывать graceful shutdown в Fiber с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.
What the interviewer is testing
- Точно объясняет, что именно делает graceful shutdown в Fiber и где это используется в Go-коде.
- Связывает graceful shutdown в Fiber с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
- Не изобретает API и опирается на реальные контракты официальной документации.