Отслеживание запросов в распределенных системах

В этой статье мы сосредоточимся на реализации архитектуры, представленной в предыдущей статье. Не торопитесь, чтобы просмотреть его.



Распределенная трассировка с OpenTelemetry - Часть 1
Общие сведения об OpenTelemetry и распределенной трассировке medium.com



Весь код можно найти в моем репозитории на github. Я буду создавать и вставлять здесь некоторые сущности для справки, они будут содержать только самые актуальные и важные части кода. Так что я действительно рекомендую вам проверить мой github для получения полной справки. Поскольку многие люди работают над улучшением OpenTelemetry каждый день, а проект все еще находится на начальной стадии, возможно, что опубликованный здесь код устареет очень быстро.

OpenTelemetry все еще находится в стадии бета-тестирования, поэтому его пока не следует использовать в производственных системах.

Вот снова диаграмма, так что вам не нужно постоянно переключаться между статьями;)

Клиент

Запросы будут исходить из нашего клиентского приложения. Это простое консольное приложение, написанное на go, основная цель которого - вызвать погодный сервис через gRPC. Это приложение инициирует диапазон, который будет распространен (распространение контекста) на погодную службу.

Пара важных частей из приведенного выше кода. В строке 18 мы настраиваем экспортер для наших трассировок. На следующем этапе я добавлю код для этой конкретной функции. В строке 20 мы создаем трассировщик для этого приложения. В строке 22 мы добавляем перехватчик для вызовов gRPC. Этот перехватчик добавит информацию о диапазоне в контекст перед отправкой запроса на сервер gRPC. Это также известно как распространение контекста. Это ключевой момент при распределенной трассировке, так как именно так соединяются промежутки. К счастью, OpenTelemetry предлагает плагины для этого, поэтому нам нужно только добавить перехватчик, и вся информация будет введена в запрос. В строке 39 мы создаем новый диапазон, который откладываем функцию .End() в строке 40. Также мы используем контекст, возвращаемый в качестве контекста, который мы передаем в запрос gRPC. В самом конце функции getCurrentWeather мы добавляем ответ как событие в созданный диапазон.

По этой сути мы можем увидеть, как экспортер настроен для Jaeger. Мы полагаемся на официальный пакет экспортера, предоставляемый OpenTelemetry. Мы будем использовать эту же функцию для создания экспортеров из client и погодного сервиса, поскольку оба они реализованы в go.

Служба погоды

Эта служба также реализована в go, поэтому многое будет похоже на client. Эта служба выполняет быстрый поиск местоположения, указанного в запросе, и вызывает службу температуры для получения случайной температуры. Он возвращает обе части информации, condition и temperature.

Это очень похоже на то, что у нас есть на client. Мы используем перехватчик для вызовов gRPC. Основное отличие состоит в том, что он будет делать противоположное тому, что он делал для client, вместо injecting информации диапазона в контексте, он extract эту информацию извлекает из него. Мы можем увидеть это в действии, когда можем вызвать span := trace.SpanFromContext(ctx) в строке 40. Этот вызов возвращает только действительный диапазон, потому что контекст (ctx) содержит информацию о диапазоне, извлеченную перехватчиком. Все сделано пакетом grpctrace. За кулисами этот пакет использует методы Extract и Inject, указанные в спецификации OpenTelemetry. В этом случае для устранения неполадок мы добавляем в диапазон разные события. Мы добавляем выбранную опцию в строку 32 (или записываем ошибку, если местоположение не найдено на нашей карте местоположений в строке 28). Кроме того, сразу после вызова службы температуры в строке 37 мы делаем то же самое, обрабатывая ошибку в случае, если что-то пойдет не так, или добавляем событие в диапазон с возвращенной температурой.

Это код для вызова службы температуры. В этом случае, поскольку служба реализована в rest api, мы используем пакет httptrace для inject информации диапазона (содержащейся в параметре ctx) в HTTP-запросе. Использование стандарта W3C для распространения контекста. Это подводит нас к последней части нашего образца.

Температурный режим

Эта служба просто обслуживает конечную точку температуры по API отдыха, реализованному на C #. В реализации контроллера нет ничего особенного. Вся магия в Startup.cs.

Как видите, реализация очень похожа на то, что было у нас в go. Мы настраиваем экспортер для Jaeger, устанавливаем выборку всегда выборки и добавляем сборщик запросов с .AddRequestCollector. Этот вызов устанавливает промежуточное ПО для добавления телеметрии ко всем полученным запросам. Это промежуточное ПО также понимает W3C формат и extracts извлекает из него информацию, если она присутствует. Так что в основном это идеально сочетается с остальными нашими услугами.

Запросы на отслеживание

Если мы продолжим и запустим все вместе, все пролеты в конечном итоге должны быть отправлены в Jaeger. Если мы переключимся на пользовательский интерфейс Jager, выберем службу client и нажмем Найти следы, мы получим следующие результаты:

Это начальное представление покажет все промежутки, связанные с этой конкретной службой. С этой точки зрения мы уже можем видеть, что диапазон от клиента на самом деле содержит диапазоны от других сервисов. Если мы нажмем на этот промежуток, чтобы проверить его детали, мы увидим что-то вроде этого:

Вверху мы видим временную шкалу, содержащую все промежутки, которые являются частью этого конкретного запроса. Это очень помогает выявить узкие места и проблемы с производительностью. Например, мы видим, что большую часть времени мы тратим на звонки в службу температуры. Если мы хотим еще больше исследовать каждый из промежутков, нам просто нужно щелкнуть по одному из них.

Это подробности из основного диапазона службы погоды. Как мы видим, события, которые были добавлены в диапазон, отображаются здесь в виде журналов. Это очень помогает устранить неполадки и понять, что произошло с этим конкретным запросом. Мы видим, что сначала этот диапазон был получен через gRPC, сразу после этого мы выбрали условие для местоположения, затем мы получили температуру от внешней службы, все завернули и отправили ответ обратно клиенту . Такой подход очень помогает, когда вы работаете с несколькими параллельными функциями. Если у нас есть один диапазон для каждой параллельной функции (горутины), мы можем легко понять, к какому из них относятся журналы. Это очень сложно, когда мы работаем только с голыми журналами, поскольку они, вероятно, будут записываться вне очереди и без особого контекста. Не бойтесь использовать это, создавайте столько интервалов, сколько вам нужно, чтобы понять, что происходит с вашими запросами.

В этом конкретном примере мы не запрашивали данные из разных сервисов одновременно, но это определенно может случиться в реальной системе. Это показывает мощь OpenTelemetry и распределенной трассировки.

Последние мысли

Распределенные системы сложно отслеживать и устранять неполадки. Журналы и метрики, хотя и являются очень полезной вещью, иногда их недостаточно, чтобы понять, что произошло с данным запросом или в данном сценарии. Коррелировать вещи сложно, особенно когда вам приходится делать это вручную или с использованием разных технологий и платформ. OpenTelemetry предлагает хороший стандарт с готовой функциональностью, чтобы помочь в этих сценариях.

Стандарт позволяет нам общаться с использованием различных протоколов и языков программирования практически без проблем. Пока вы полагаетесь на стандарт, все будет работать с обеих сторон, и все будет легко согласовано.