Микросервисы на Go: что изучить перед переходом от монолита к отдельным сервисам
Переход от монолита к микросервисам на Go — это умение разделить работающее приложение на сервисы, которые разрабатывают, выкладывают и масштабируют независимо, и написать каждый так, чтобы он переживал сбои соседей. Перед переходом стоит изучить, когда модульный монолит выгоднее, как провести границы сервисов по предметной области, как сервисы обмениваются данными через REST, gRPC и брокеры сообщений, как обойтись без общей базы данных и как наблюдать за системой. Со стороны Go понадобятся пакет context, корректная остановка процесса и библиотека gRPC-go.
Содержание

Главное заблуждение, с которым приходят к этой теме: микросервисы делают систему быстрее. Чаще наоборот: вызов функции превращается в сетевой запрос, который может задержаться, потеряться или прийти дважды, а транзакция распадается на шаги в разных сервисах. Взамен команда выпускает свою часть без согласования с остальными. Поэтому изучение начинают не с Kubernetes, а с вопроса, стоит ли делить систему вообще.
Когда монолит лучше
Мартин Фаулер в заметке MonolithFirst (3 июня 2015 года) описал два наблюдения: почти все успешные микросервисные системы начинались с разросшегося монолита, а почти все системы, сразу построенные из микросервисов, столкнулись с серьёзными проблемами. Цену поддержки множества сервисов он называет «микросервисной надбавкой», а монолит советует как способ найти верные границы: пока продукт меняется, переносить их внутри одного кода гораздо дешевле, чем между сервисами.
Промежуточный вариант — модульный монолит. В Go его удобно строить на пакетах: каждый модуль живёт в своём каталоге, наружу открывает только интерфейс, а детали прячет в каталоге internal, который нельзя импортировать из чужих пакетов. Бинарник и выкладка одни, но модуль можно вынести, не переписывая соседей. Обратное движение тоже бывает: по пересказу InfoQ от 3 мая 2023 года, команда Prime Video собрала распределённый сервис контроля качества видео в один процесс и сильно сократила расходы. Речь шла об одном сервисе, а не обо всём Prime Video.
Признаки того, что делить пора, видны в работе команд. Проверяйте их вместе: одно условие само по себе ещё не повод заводить сервис.
- Несколько команд мешают друг другу при выпусках, и релиз одной части ждёт тестов другой.
- Одна часть требует другого масштабирования, например рассылки.
- Границы модулей не менялись несколько месяцев.
- Есть CI/CD, контейнеры и мониторинг — без них каждый сервис добавит ручной работы.
Границы сервисов и способы связи
Границы проводят по предметной области, а не по техническим слоям. В предметно-ориентированном проектировании (DDD) для этого есть понятие ограниченного контекста: участок системы со своим словарём и своими данными. «Клиент» в оплате — плательщик с реквизитами, а в уведомлениях — адресат с каналами связи. Если разделить систему на «сервис базы данных» и «сервис валидации», любая доработка затронет оба — получится распределённый монолит.
Переход делают постепенно, по паттерну, который Фаулер назвал Strangler Fig — «фикус-душитель»: новый сервис вырастает рядом со старым кодом и забирает у него функции по одной. Способ связи выбирают по задаче. Запрос, на который нужен немедленный ответ, делают через REST или gRPC — во втором случае контракт описывают в файле .proto и генерируют по нему код. Событие вроде «заказ оплачен» лучше отправлять через брокер сообщений — Kafka, RabbitMQ или NATS: отправитель не ждёт получателя и не падает вместе с ним.
Учебный пример: выделяем сервис уведомлений
Ситуация учебная. Монолит интернет-магазина после оплаты заказа отправляет письмо и SMS прямо внутри обработчика оплаты. Медленный почтовый провайдер подвешивает оформление заказа, а ошибка SMS-шлюза откатывает всю операцию. Задача — вынести уведомления в отдельный сервис на Go так, чтобы оплата не зависела от рассылки.
Сначала внутри монолита код рассылки прячут за интерфейсом Notifier и убирают прямые вызовы из обработчика. Затем вместо отправки письма монолит в той же транзакции, что и смена статуса заказа, записывает событие OrderPaid в таблицу outbox. Отдельный процесс читает эту таблицу и публикует события в брокер. Событие не потеряется при недоступном брокере и не уйдёт при откате транзакции — это паттерн transactional outbox.
Новый сервис notifier читает события из брокера и рассылает сообщения. Брокеры обычно гарантируют доставку «хотя бы один раз», поэтому сервис хранит идентификаторы обработанных событий и пропускает повторы — иначе клиент получит два SMS. Настройки каналов пользователя notifier запрашивает у монолита по gRPC с таймаутом, заданным через context. Саги — цепочки шагов с компенсирующими действиями на случай сбоя — здесь не нужны: рассылка ничего не откатывает. Они понадобятся, когда заказ, оплата и склад окажутся в разных сервисах.
Результат проверяют так: остановленный notifier не мешает оплате, а после запуска рассылает накопившееся; событие, отправленное дважды, даёт одно письмо.
Что даёт Go и где ловушка при остановке
Go удобен для сервисов: программа собирается в один небольшой бинарник, образ контейнера компактен, а context проходит через весь код — от входящего запроса до базы данных. В gRPC-go дедлайн из context передаётся на сервер вместе с запросом, поэтому отмена запроса доходит по цепочке до последнего сервиса.
Меньше известна ловушка остановки. Метод http.Server.Shutdown сначала закрывает все слушающие сокеты, потом простаивающие соединения и ждёт завершения активных запросов. Соединения, захваченные через hijack, например WebSocket, он не ждёт — их нужно закрывать отдельно. Сигнал о завершении удобно ловить через signal.NotifyContext, доступный с Go 1.16.
В Kubernetes этого мало. В разделе о завершении подов документации Kubernetes сказано, что исключение пода из EndpointSlice начинается одновременно с остановкой контейнеров, а не до неё. Сервис получает SIGTERM и закрывает сокет, а балансировщик ещё какое-то время шлёт туда запросы — при каждой выкладке часть клиентов получает ошибки соединения. Обычное решение — хук preStop с паузой в несколько секунд перед сигналом. Встроенное действие sleep для хуков появилось в Kubernetes 1.29 и стало стабильным в 1.34. Время хука входит в terminationGracePeriodSeconds, по умолчанию 30 секунд, поэтому пауза и таймаут Shutdown вместе должны укладываться в этот срок, иначе процесс завершат принудительно.
Наблюдаемость: логи, метрики, трассировка
В микросервисах запрос проходит через несколько процессов, поэтому до выделения первого сервиса договоритесь о трёх вещах: структурированные логи (в Go — пакет log/slog), метрики запросов, ошибок и времени ответа, распределённая трассировка. Открытый стандарт OpenTelemetry описывает все три сигнала и даёт SDK для Go: идентификатор трассы передаётся в context, а по trace_id в логах собирают весь путь запроса.
В каком порядке осваивать
Последовательность ниже повторяет путь реального переноса: сначала порядок внутри одного процесса, затем сеть, потом данные и эксплуатация. Если перескочить к Kubernetes, не умея провести границу модуля, получится распределённый монолит с более сложной отладкой.
- Модульный монолит на Go: пакеты,
internal, интерфейсы между модулями, тесты на границах. - Сеть: HTTP-сервер, gRPC и protobuf, таймауты и повторы через
context. - Брокер сообщений, гарантии доставки и идемпотентная обработка.
- Данные: база на сервис, outbox, саги, миграции без остановки.
- Docker, корректная остановка, развёртывание в Kubernetes.
- Логи, метрики и трассировка с OpenTelemetry.
Тренироваться удобно на учебном монолите: Docker Compose поднимет базу, брокер и два сервиса на ноутбуке. Тема переноса разобрана и на курсе Слёрма «Проектирование микросервисной архитектуры: на примере разделения монолита на Go». В карточке на Учи.Онлайн нет программы, длительности и цены, поэтому состав практики и требования к опыту уточните у школы.
Упражнения и ошибки новичков
Хорошие упражнения проверяют систему на сбоях. Остановите брокер посреди обработки и убедитесь, что события дождались его в outbox. Отправьте одно событие дважды и проверьте, что рассылка не повторилась. Выкатите новую версию сервиса под нагрузочным тестом и посчитайте ошибки: если они есть, ищите причину в порядке остановки.
Типичные ошибки: сервисы делят одну базу, и изменение таблицы ломает соседей; синхронные вызовы тянутся через пять сервисов, и отказ любого роняет запрос; HTTP-клиент работает без таймаута; событие отправляют после коммита без outbox и теряют при сбое между шагами.
Если основы Go уже есть, а нужна структурированная практика с сервисами, подойдёт курс TeachMeSkills «GO разработчик»: 4,5 месяца и 136 академических часов, конкурентность, REST API, PostgreSQL и Redis, а в продвинутой части — микросервисы, gRPC с protobuf, Docker и Kubernetes; в конце — дипломный проект. Брокеры и трассировка в программе не названы.
Как навык влияет на доход
За опыт с микросервисами не платят отдельной строкой: он отличает разработчика, которому доверяют проектирование и выкладку сервисов, от того, кто дописывает обработчики в готовом коде. Чтобы оценить его вес, сравните вакансии разных уровней в своём городе: где gRPC, брокеры и Kubernetes стоят в обязательных требованиях, а где в пожеланиях.
Ориентир по рынку даёт исследование «Хабр Карьеры» за первое полугодие 2026 года по 45 226 зарплатам, которые специалисты сами указали в калькуляторе: медиана бэкенд-разработчиков — 278 000 ₽ в Москве, 251 000 ₽ в Санкт-Петербурге и 220 000 ₽ в регионах, а у специалистов, назвавших основным языком Go, — 325 000 ₽ без разбивки по городам. Это фактические зарплаты всех уровней, а не предложения в вакансиях, и разбивки Go по квалификации в отчёте нет.
Как выбрать обучение
Тем, кто приходит в Go с нуля или из другого стека, полезнее длинная программа, где микросервисы идут после основ языка и баз данных. На курсе PurpleSchool «Backend-разработчик на Go» заявлено девять месяцев при нагрузке 10–20 часов в неделю: основы и продвинутый Go, HTTP-сервер, базы данных, тесты API, затем Docker, Kubernetes с Helm и микросервисы на RabbitMQ. Основной проект — API сокращателя ссылок; gRPC, Kafka и наблюдаемость не упомянуты, цены на странице нет.
При сравнении программ проверьте, есть ли учебный монолит, который предстоит делить, разбирают ли outbox, идемпотентность и корректную остановку и смотрит ли код живой ревьюер. Чего нет в программе, придётся добирать по документации.
Сравнить школы по формату, цене и отзывам можно в рейтинге онлайн-школ с обучением Go — он охватывает язык целиком, а не только микросервисы. Прежде чем выбирать, попробуйте выделить сервис уведомлений из учебного примера в своём проекте: где застрянете — на границах, данных или эксплуатации, — то и ищите в программе.