В современном мире все больше IT-проектов переходят на микросервисную архитектуру. При таком подходе появляется ряд вопросов — как избитых, так и не самых очевидных. Как обеспечить корректную работу системы из большого числа микросервисов? Каким способом и с помощью каких технологий разворачивать этот зоопарк? Как отследить узкие места в производительности?

Меня зовут Максим, я Java-разработчик в SimbirSoft. Сейчас работаю на финтех-проекте, занимаюсь созданием бизнес-логики, иногда подключаюсь к различным техническим задачам. В этой статье я кратко и понятно опишу, что такое трассировки, как устроен Jaeger, а также расскажу про недавно появившийся новый стандарт распределенного трейсинга и мониторинга — OpenTelemetry.

Материал будет полезен начинающим разработчикам. Из него вы узнаете, как с небольшими усилиями подключить трассировки к своему проекту. Речь пойдет об инструменте из большого блока «‎Наблюдаемость» (Monitoring), а конкретно — о трассировках.

Что такое трассировка?

Трассировка — это метод, который используют для профилирования и мониторинга приложений, особенно тех, которые созданы с использованием архитектуры микросервисов. Трассировка помогает точно определить, где возникают сбои и что вызывает низкую производительность.

Трассировки оперируют понятиями span и trace. Span — это некоторое действие, которое содержит в себе следующие части:

  • название сервиса;

  • уникальный ID;

  • ID родительского спана;

  • название вызываемого метода;

  • время начала и окончания.

Трассировка — это набор/список span, которые соединены отношением родитель/потомок (его также можно рассматривать как направленный ациклический граф отрезков). Трассировки указывают, как запросы распространяются через наши службы и другие компоненты.

Чем она полезна?

Трассировка помогает обнаружить узкие места в производительности. Это возможно благодаря следующим функциям:

  • отслеживание обращения к ресурсам (БД, внешняя система, файлы и т.д);

  • отслеживание последовательного вызова нескольких сервисов;

  • сбор массива данных для последующего анализа при помощи метрик;

  • возможность получить наглядное представление о том, в какой последовательности вызываются сервисы.

Архитектура Jaeger

Также стоит упомянуть о Jaeger. Jaeger — это платформа распределенной трассировки с открытым исходным кодом, созданная Uber еще в 2015 году. Она состоит из инструментальных SDK, серверной части для сбора и хранения данных, пользовательского интерфейса для визуализации данных и платформы Spark/Flink для совокупного анализа трассировки.  

Основные части платформы:

  1. Инструментальный SDK — библиотеки, необходимые для сбора метрик.

  2. Jaeger-agent — локальный агент. Если агента нет, то трейсы всех сервисов на этой машине обычно выключены.

  3. Jaeger-collector — в него все агенты посылают собранные трейсы, а он кладет их в выбранную БД.

  4. Jaeger-query — сервис, который ходит в базу данных и отдает уже собранные трейсы для анализа.

  5. Jaeger-ui — веб-интерфейс для поиска и просмотров трейсов. Он ходит в Jaeger-query.

Локальный запуск Jaeger при помощи Docker в приложениях Spring Boot

Для начала создадим два простых сервиса — Consumer и Producer. Первый будет обращаться по http ко второму, получать слово “hello” и выводить слово “hello-world”.

Рассмотрим контроллер первого сервиса — Consumer. Все стандартно — RestController, GetMapping, Lombok и т.д., кроме feignClient. Способы взаимодействия между сервисами — тема не этой статьи, но в нашем случае используется обычный feignClient. В двух словах, spring-cloud-starter-openfeign позволяет с минимальными усилиями обеспечить сетевое взаимодействие между сервисами.

@RestController
@RequiredArgsConstructor
public class ConsumerController {

    private final ProducerFeign producerFeign;

    @GetMapping(value = "/hello-world")
    public String getHelloWorld() {
        return producerFeign.getHello() + " world";
    }
}

Обратимся к клиенту. При обращении к методу getHello() по localhost:8081/hello получаем слово “hello”.

@FeignClient(name = "producer-feign", url = "localhost:8081")
public interface ProducerFeign {

    @GetMapping(value = "/hello")
    String getHello();
}

Контроллер нашего второго сервиса (клиента, к которому обращается первый) — Producer.

@RestController
@RequiredArgsConstructor
public class ProducerController {

    @GetMapping(value="/hello")
    public String getHello() {
        return "hello";
    }
}

Локальный запуск Jaeger при помощи Docker

Jaeger поставляется в качестве готового образа Docker. Для запуска образа Jaeger выполняем следующее: 

docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-p 5775:5775/udp \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14250:14250 \
-p 14268:14268 \
-p 14269:14269 \
-p 9411:9411 \
jaegertracing/all-in-one:1.6

Далее необходимо запустить наше приложение. В этом руководстве будет использован новый стандарт OpenTelemetry. Все, что нужно сделать — это запустить jar-приложение с параметром javaagent. 

OpenTelemetry — относительно новый стандарт. Он включает в себя распределенный трейсинг и мониторинг и совместим с OpenTracing и OpenCensus. Более того, последние прекращают поддержку в течение двух лет, что неотвратимо приближает нас к переходу на OpenTelemetry.

Стандарт, по сути, представляет собой некую прослойку между потребителями метрик/трейсов и их поставщиками.

В документации OpenTelemetry можно найти параметры, с которыми запускается jar (например, OTEL_TRACES_EXPORTER, OTEL_SERVICE_NAME и т.д.). Параметр Dotel.traces.exporter определяют куда opentelemetry будет складывать трейсы, в нашем случае это Jaeger. 

Запуск первого сервиса:

java -javaagent:{your-path-to-agent}/opentelemetry-javaagent.jar    -Dotel.traces.exporter=jaeger -Dotel.service.name=customer-consumer -jar customer-consumer-0.0.1-SNAPSHOT.jar

Запуск второго сервиса:

java -javaagent:{your-path-to-agent}/opentelemetry-javaagent.jar    -Dotel.traces.exporter=jaeger -Dotel.service.name=customer-producer -jar customer-producer-0.0.1-SNAPSHOT.jar

Для перехода в пользовательский интерфейс Jaeger используем http://localhost:16686/

Вывод 

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

Чтобы получить максимальную отдачу от использования инструмента, ваши сервисы должны отправлять трассировки на центральную панель мониторинга — например, Jaeger. В этой статье мы рассмотрели лишь часть функциональности OpenTelemetry. На самом деле, возможности стандарта намного шире.

Если вам интересна эта тема — сохраните полезные ссылки:

Спасибо за внимание!

Больше полезных материалов для backend-разработчиков мы также публикуем в наших соцсетях – ВК и Telegram.

Комментарии (3)


  1. ris58h
    06.06.2023 14:19
    +9

    вы узнаете, как с небольшими усилиями подключить трассировки к своему проекту

    Нарратор: они не узнают.

    Самое главное упустили. Вот вам ссылка на спеку, вот команда запуска какого-то контейнера, а вот команда запуска агента. И что с этим делать? Пример хоть бы один рабочий. Вот 2 микросервиса у меня. Один дёргает другого по http. Как эту связь увидеть?


    1. SSul Автор
      06.06.2023 14:19

      Спасибо за замечание! Обновили статью, добавили описание методов контроллера, цепочку вызова и команду по запуску сервисов через javaagent. Надеемся, это поможет вам и другим читателям)


      1. ris58h
        06.06.2023 14:19

        Уже лучше. Теперь бы ещё рассказать как эта магия работает под капотом. Если я сервис ручками через соккет дёргаю - заработает или всё же есть какие-то ограничения?