Go очень популярен среди разработчиков бэкенда, а его сообщество с каждым днем только растет.Лично я предпочитаю писать код на Go.
В этом кратком руководстве описывается то, как происходит нормальное завершение работы приложений на Go. Процесс этот на самом деле очень простой. Пошагово разберем его, чтобы понять, как реализуется нормальное завершение работы на Go. Предполагается, что вы уже знаете из других языков программирования для чего нужно нормальное завершение работы приложений.
Сигналы
Сигнал — это событие, генерируемое системами UNIX и Linux в ответ на какое-либо условие, и Go прослушивает эти события на уровне приложения.
Существует много сигналов, но нас интересуют только сигналы завершения работы. Рассмотрим их вкратце:
SIGTERM является общим сигналом завершения программы, который передается почти для всех событий завершения работы (за исключением приведенных ниже).
SIGKILL является сигналом завершения, который передается для событий «немедленного выхода» и, как правило, не должен блокироваться.
SIGINT передается, когда пользователь вводит сигнал прерывания (например, Ctrl+C). SIGINT аналогичен SIGTERM, но предназначен для пользовательских событий.
SIGQUIT передается, когда пользователь вводит сигнал выхода (Ctrl+D). SIGQUIT аналогичен SIGKILL, но предназначен для пользовательских событий (таких как принудительный выход).
Теоретически для эксплуатационной среды должно быть достаточно прослушивания SIGTERM. Поды как некие запущенные процессы в кластере Kubernetes, представляющие собой обёртку связанных друг с другом контейнеров, передают сигнал SIGTERM при завершении работы.
SIGINT безопасен для прослушивания, и его хорошо использовать для локальной отладки.
Таким образом, SIGINT и SIGTERM охватывают почти все возможные сценарии: пока мы перехватываем и обрабатываем сигналы, приложение должно быть в порядке.
Использование каналов для перехватывания сигналов
Вам знакомо понятие конкурентности в Go? Тогда вы уже знаете о каналах.
Здесь понадобится блок кода для прослушивания сигнала завершения работы и выполнения нашего пользовательского поведения (освобождения ресурсов) на основе этой информации.
Каналы идеально подходят для этой задачи, ведь они ждут (из-за их блокирующего поведения) получения сообщения.
Поэтому объединим каналы сos.Signal
указанными выше сигналами:
При запуске нашего приложения не будет выведено Done!
(«Готово!»), пока канал gracefulShutdown
не дождется получения сообщения.
Но ведь gracefulShutdown
прослушивает SIGINT и SIGTERM. Поэтому приложение будет ждать, пока программа не завершится.
Вот что будет выводиться при закрытии приложения:
Получилось!
Но обычно этого недостаточно, нужно еще освободить ресурсы из используемых источников, таких как внешние API, базы данных и т. д.
Вот код непосредственно из документации Go:
Вызов cancel()
позволяет освободить ресурсы, связанные с определенным контекстом. Поэтому для достижения желаемого поведения объединим канал нормального завершения работы gracefulshutdown
с нашим контекстом:
Дожидаемся сигнала завершения работы и правильно освобождаем ресурсы.
Вот как это делается:
А теперь настройте этот код под себя. Допустим, это почасовая работа и вам нужно отправить сообщение «завершено» в очередь сообщений. Делается это следующим образом:
Готово!
Спасибо за внимание! ❤️
Читайте также:
- Основы Go: ввод-вывод файловой системы
- Использование конкурентности при создании API в Go
- Создаем настраиваемую цепочку обязанностей в Go
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Emre Tanriverdi: Graceful shutdown in Go