import
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
## 👋 Привет! Я Олег Козырев
|
||||
|
||||
Staff Golang Engineer · Ex Ozon, Avito, Tinkoff
|
||||
|
||||
📺 [YouTube](https://www.youtube.com/@olezhek28go) · 💬 [Telegram](https://t.me/olezhek28go)
|
||||
|
||||
---
|
||||
|
||||
# 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
|
||||
|
||||
1. `signal.NotifyContext` перехватывает **SIGINT** (Ctrl+C) и **SIGTERM** (Kubernetes)
|
||||
2. `server.Shutdown()` — перестаёт принимать новые соединения и дожидает текущие запросы (таймаут 15с)
|
||||
3. `closer.CloseAll()` — закрывает все ресурсы в обратном порядке: кэш, затем БД (таймаут 10с)
|
||||
4. Суммарный бюджет: 15с + 10с = 25с из 30с Kubernetes grace period
|
||||
|
||||
**Паттерн «двойной Ctrl+C»:** первый — graceful shutdown, второй — мгновенное завершение (для разработки, если shutdown завис).
|
||||
|
||||
## Как попробовать
|
||||
|
||||
```bash
|
||||
# Запуск
|
||||
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 — база всегда закрывается последней, потому что создаётся первой.
|
||||
Reference in New Issue
Block a user