4.3 KiB
4.3 KiB
👋 Привет! Я Олег Козырев
Staff Golang Engineer · Ex Ozon, Avito, Tinkoff
Graceful Shutdown Demo
Учебный Go-проект, демонстрирующий паттерн graceful shutdown — корректное завершение приложения с дожиданием текущих запросов и освобождением ресурсов.
Что внутри
Полноценное HTTP-приложение с несколькими слоями:
- HTTP API — три эндпоинта:
/health,/users/me,/slow - Сервисы — бизнес-логика (UserService, AuthService, NotificationService)
- Репозитории — слой данных (UserRepo, SessionRepo, NotificationRepo)
- Инфраструктура — база данных (PostgreSQL), кэш (Redis), шина событий (EventBus)
- DI-контейнер — ленивая инициализация зависимостей с автоматической регистрацией в closer
- Closer — менеджер graceful shutdown (LIFO-порядок закрытия ресурсов)
Как работает graceful shutdown
signal.NotifyContextперехватывает SIGINT (Ctrl+C) и SIGTERM (Kubernetes)server.Shutdown()— перестаёт принимать новые соединения и дожидает текущие запросы (таймаут 15с)closer.CloseAll()— закрывает все ресурсы в обратном порядке: кэш, затем БД (таймаут 10с)- Суммарный бюджет: 15с + 10с = 25с из 30с Kubernetes grace period
Паттерн «двойной Ctrl+C»: первый — graceful shutdown, второй — мгновенное завершение (для разработки, если shutdown завис).
Как попробовать
# Запуск
go run cmd/main.go
# В другом терминале — медленный запрос
curl localhost:8080/slow
# Пока запрос висит — нажмите Ctrl+C в терминале сервера
# Без graceful shutdown: curl получит "connection reset by peer"
# С graceful shutdown: curl дождётся ответа "готово!" — 200 OK
Структура проекта
cmd/
main.go # Точка входа
internal/
app/
app.go # Жизненный цикл приложения, graceful shutdown
di.go # DI-контейнер с ленивой инициализацией
api/
server.go # HTTP-хендлеры и маршрутизация
closer/
closer.go # Менеджер закрытия ресурсов (LIFO)
config/
config.go # Конфигурация (DSN, Redis, HTTP-порт)
database/
database.go # Абстракция базы данных
cache/
cache.go # Абстракция кэша (Redis)
events/
bus.go # Шина событий (pub/sub)
service/
user.go # Бизнес-логика пользователей
auth.go # Авторизация
notification.go # Уведомления
repository/
user.go # Доступ к данным пользователей
session.go # Доступ к данным сессий
notification.go # Доступ к данным уведомлений
Граф зависимостей
Handler
├── UserService
│ ├── UserRepo → DB
│ ├── AuthService
│ │ ├── UserRepo → DB
│ │ ├── SessionRepo → DB + Cache
│ │ ├── Cache
│ │ └── EventBus
│ └── EventBus
├── AuthService
└── NotificationService
├── NotificationRepo → DB
├── UserService
└── EventBus
Порядок закрытия (LIFO): EventBus → Cache → DB — база всегда закрывается последней, потому что создаётся первой.