Представьте, что вы заказали пиццу через приложение. Вы нажимаете кнопку "Заказать", и ваш запрос отправляется в микросервис, который отвечает за оформление заказа. Этот микросервис запрашивает данные другого сервиса – того, который управляет списком пицц. Затем включается ещё один сервис, который рассчитывает доставку, и затем ещё один – для оплаты. Итак, простой на вид запрос проходит через много сервисов, каждый из которых может оставить свой "след".
Распределённая трассировка (Distributed Tracing) – это процесс слежения за "путешествием" вашего запроса через все эти сервисы. Она помогает понять, через какие компоненты проходит запрос, сколько времени он тратит на каждом этапе и где именно он может тормозить.
Основные задачи распределённой трассировки:
- Показать весь путь выполнения запроса.
- Обнаружить узкие места и медленные операции.
- Диагностировать ошибки и сбои.
- Улучшить производительность и оптимизировать архитектуру.
Если говорить о распределённой трассировке в мире микросервисов, она становится основным инструментом, чтобы не потеряться в сложной паутине взаимодействий между сервисами.
Фундаментальные понятия
В распределенной трассировке есть два ключевых понятия:
- Trace (трасса) – это полный путь запроса от начала до конца. Например, запрос на оформление заказа может начинаться в веб-приложении и заканчиваться в сервисе доставки, а Trace объединяет все эти этапы.
- Span (спан) – это отдельный шаг внутри Trace. Каждый вызов микросервиса, обращение в базу данных или вызов объекта через HTTP считается Span'ом.
На практике это выглядит как дерево: каждый Trace содержит множество Span’ов, и они разбиваются на дочерние и родительские действия.
Зачем нужна распределённая трассировка?
- Разбираться в сложных взаимодействиях: когда запрос "застревает", трассировка поможет понять, где именно пошло что-то не так. Например, сервис доставки может задерживать ответ из-за медленного соединения с базой данных.
- Уменьшить время простоя: трассировка помогает быстро понять, какие компоненты ломаются, и где именно находятся узкие места.
- Оптимизировать производительность: замеры времени выполнения операций позволяют находить участки кода или взаимодействий, которые нужно улучшить.
- Обеспечить прозрачность: если вы работаете в команде, трассировка делает систему более понятной для всех. Всё, что происходит между сервисами, документируется.
Основные компоненты распределённой трассировки
Span и Trace: тандем, который всё объясняет.
Немножко о Span:
- Span – это единичное действие, например, вызов REST API или операция записи в базу данных.
- У каждого Span есть уникальный идентификатор, который связывает его с трейсом и другими спанами.
И немного о Trace:
- Trace объединяет множество Span'ов в одно целое.
- У Trace тоже есть свой уникальный идентификатор, чтобы показать, что все Spans принадлежат одному запросу.
Пример:
Trace ID: 987654321
├── Span ID: 1 (Customer Service - обработка заказа)
├── Span ID: 2 (Pizza Service - проверка наличия пиццы)
├── Span ID: 3 (Delivery Service - расчёт доставки)
└── Span ID: 4 (Payment Service - обработка оплаты)
Наблюдение: каждый шаг длиною в Span.
Инструменты для реализации распределённой трассировки
Есть несколько инструментов, которые упрощают реализацию трассировки в микросервисах. Рассмотрим два самых популярных:
1. Zipkin
Zipkin – это инструмент для сбора и визуализации данных трассировки. Он позволяет хранить и анализировать данные о времени выполнения каждого запроса.
- Плюсы: Простота настройки, поддержка множества языков программирования.
- Минус: Ограниченные возможности для метрик и мониторинга (только трассировка).
Официальная документация Zipkin
2. OpenTelemetry
OpenTelemetry – это более современный подход, который объединяет трассировку, метрики и логи в одном инструменте. Его можно считать универсальным решением для Observability.
- Плюсы: Поддержка нескольких стандартов, гибкость.
- Минус: Более сложная настройка по сравнению с Zipkin.
Официальная документация OpenTelemetry
Как работает распределённая трассировка?
Процесс работы можно описать таким образом:
- Генерация Trace ID: когда запрос попадает в систему, создаётся уникальный идентификатор для Trace.
- Генерация Span ID: каждое действие в рамках этого Trace получает свой уникальный Span ID.
- Передача контекста: Trace ID и Span ID передаются между сервисами с помощью заголовков HTTP.
- Сбор данных: каждый сервис записывает данные о времени выполнения, ошибках и других событиях.
- Анализ: все данные отправляются в инструмент (например, Zipkin), где вы можете визуализировать весь процесс.
Пример взаимодействия микросервисов с трассировкой
Представьте такую схему взаимодействий:
Client -> Order Service -> Inventory Service -> Payment Service
Сначала Клиент отправляет запрос в Order Service (микросервис оформления заказа). Тот, в свою очередь, вызывает:
- Inventory Service для проверки остатков на складе.
- Payment Service для подтверждения платежа.
Распределённая трассировка создаст дерево запросов, которое покажет:
- Сколько времени занял Order Service.
- Где именно запрос задержался (например, на этапе проверки склада).
- Если возникла ошибка, в каком сервисе она произошла.
Как это помогает в реальной жизни?
Распределённая трассировка становится особенно полезной, когда система разрастает десятки и сотни микросервисов. В такой системе:
- Ошибки могут происходить где угодно.
- Ручной поиск проблемы становится невозможным.
- Тестирование всех взаимодействий становится сложным.
Трассировка берёт на себя задачу "развешивания ярлыков" на каждом запросе и предоставляет разработчику всё необходимое для быстрого анализа.
На следующей лекции мы поговорим о том, как интегрировать Spring Boot с Zipkin и Sleuth. Для этого мы возьмем несколько микросервисов, подключим библиотеки трассировки и научимся "видеть" запросы в интерфейсе Zipkin. Готовьтесь к практике – будет интересно!