Рубрики
Без рубрики

Весенний облачный сыщик в монолитном приложении

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

Автор оригинала: Tim Schimandle.

Весенний облачный сыщик в монолитном приложении

1. Обзор

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

И для этого writeup мы собираемся сосредоточиться на использовании сыщика в монолитном приложении, а не через микрослужбы .

У всех нас был неудачный опыт, когда мы пытались диагностировать проблему с запланированной задачей, многотемной операцией или сложным веб-запросом. Часто, даже при регистрации, трудно сказать, какие действия должны быть связаны друг с другом, чтобы создать один запрос.

Это может сделать диагностика сложного действия очень трудно или даже невозможно. Часто приводит к решениям, таким как передача уникального идентификатора каждому методу в запросе на идентификацию журналов.

В приходит Сыщик . Эта библиотека позволяет идентифицировать журналы, относящиеся к определенному задание, потоку или запросу. Сыщик легко интегрируется с такими структурами лесозаготовок, как Логбэк и SLF4J добавление уникальных идентификаторов, которые помогают отслеживать и диагностировать проблемы с помощью журналов.

Давайте посмотрим, как это работает.

2. Настройка

Начнем с создания Весенняя загрузка веб-проект в нашем любимом IDE и добавление этой зависимости к нашей пом.xml файл:


    org.springframework.cloud
    spring-cloud-starter-sleuth

Наше приложение работает с Весенняя загрузка и родительский пом предоставляет версии для каждой записи. Последнюю версию этой зависимости можно найти здесь: весна-облако-стартер-сыщик . Чтобы увидеть весь POM проверить проект на Гитхуб .

Кроме того, давайте добавим имя приложения, чтобы инструктировать Сыщик для идентификации журналов этого приложения.

В нашем application.properts файл добавить эту строку:

spring.application.name=Baeldung Sleuth Tutorial

3. Конфигурации сыщика

Сыщик способен усиливать журналы во многих ситуациях. Начиная с версии 2.0.0, Весенний Облачный Сыщик использует Храбрый как библиотека отслеживания, которая добавляет уникальные идентификаторы к каждому веб-запросу, который входит в наше приложение. Кроме того, команда Spring добавила поддержку для обмена этими идентификаторами через границы потоков.

Следы можно рассматривать как один запрос или задание, которое срабатывает в приложении. Все различные шаги в этом запросе, даже через границы приложений и потоков, будут иметь один и тот же traceId.

Spans, с другой стороны, можно рассматривать как разделы работы или запроса. Один след может состоять из нескольких пролетов, каждый из которых соотносится с определенным шагом или разделом запроса. Используя идентификаторы трассировки и диапазона, мы можем точно определить, когда и где находится наше приложение при его обрабатывает запрос. Сделать чтение наших журналов намного проще.

В наших примерах мы изучить эти возможности в одном приложении.

3.1. Простой веб-запрос

Во-первых, давайте создадим класс контроллера, чтобы быть точкой входа для работы с:

@RestController
public class SleuthController {

    @GetMapping("/")
    public String helloSleuth() {
        logger.info("Hello Sleuth");
        return "success";
    }
}

Давайте за запустите наше приложение и перейдите на “http://localhost:8080”. Следите за журналами для вывода, который выглядит так:

2017-01-10 22:36:38.254  INFO 
  [Baeldung Sleuth Tutorial,4e30f7340b3fb631,4e30f7340b3fb631,false] 12516 
  --- [nio-8080-exec-1] c.b.spring.session.SleuthController : Hello Sleuth

Это выглядит как обычный журнал, за исключением части в начале между скобками. Это основная информация, которая Весенний сыщик добавил. Эти данные следуют формату:

«Название приложения, traceId, spanId, экспорт»

  • Название приложения — Это имя, которое мы установили в файле свойств и может быть использовано для агрегирования журналов из нескольких экземпляров одного и того же приложения.
  • TraceId – Это идентификатор, который присваивается одному запросу, работе или действию. Что-то вроде каждого уникального пользователя, инициированного веб-запрос будет иметь свои собственные traceId .
  • SpanId – Отслеживает единицу работы. Подумайте о запросе, который состоит из нескольких шагов. Каждый шаг может иметь свою собственную spanId и отслеживаться индивидуально. По умолчанию любой поток приложений будет начинаться с тех же TraceId и SpanId.
  • Экспортные – Это свойство является boolean, который указывает, является ли этот журнал был экспортирован в агрегатор, как Зипкин . Зипкин выходит за рамки данной статьи, но играет важную роль в анализе журналов, созданных Сыщик .

К настоящему времени, вы должны иметь некоторое представление о силе этой библиотеки. Давайте рассмотрим другой пример, чтобы продемонстрировать, насколько неотъемлемой частью этой библиотеки является журналирование.

3.2. Простой веб-запрос с доступом к услугам

Начнем с создания сервиса с помощью одного метода:

@Service
public class SleuthService {

    public void doSomeWorkSameSpan() {
        Thread.sleep(1000L);
        logger.info("Doing some work");
    }
}

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

@Autowired
private SleuthService sleuthService;
    
    @GetMapping("/same-span")
    public String helloSleuthSameSpan() throws InterruptedException {
        logger.info("Same Span");
        sleuthService.doSomeWorkSameSpan();
        return "success";
}

Наконец, перезапустите приложение и перейдите на “http://localhost:8080/same-span”. Следите за выходом журнала, который выглядит так:

2017-01-10 22:51:47.664  INFO 
  [Baeldung Sleuth Tutorial,b77a5ea79036d5b9,b77a5ea79036d5b9,false] 12516 
  --- [nio-8080-exec-3] c.b.spring.session.SleuthController      : Same Span
2017-01-10 22:51:48.664  INFO 
  [Baeldung Sleuth Tutorial,b77a5ea79036d5b9,b77a5ea79036d5b9,false] 12516 
  --- [nio-8080-exec-3] c.baeldung.spring.session.SleuthService  : Doing some work

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

Это поведение по умолчанию, один запрос получает одну traceId и spanId . Но мы можем вручную добавлять пролеты, как мы считаем нужным. Давайте рассмотрим пример, который использует эту функцию.

3.3. Вручную Добавление span

Для начала добавим новый контроллер:

@GetMapping("/new-span")
public String helloSleuthNewSpan() {
    logger.info("New Span");
    sleuthService.doSomeWorkNewSpan();
    return "success";
}

А теперь добавим новый метод в наш сервис:

@Autowired
private Tracer tracer;
// ...
public void doSomeWorkNewSpan() throws InterruptedException {
    logger.info("I'm in the original span");

    Span newSpan = tracer.nextSpan().name("newSpan").start();
    try (SpanInScope ws = tracer.withSpanInScope(newSpan.start())) {
        Thread.sleep(1000L);
        logger.info("I'm in the new span doing some cool work that needs its own span");
    } finally {
        newSpan.finish();
    }

    logger.info("I'm in the original span");
}

Обратите внимание, что мы также добавили новый объект, Трейсир . трассировщик экземпляр создается Весенний сыщик во время запуска и доступна нашему классу через инъекцию зависимости.

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

Перезапустите приложение и перейдите на “http://localhost:8080/new-span”. Следите за выходом журнала, который выглядит так:

2017-01-11 21:07:54.924  
  INFO [Baeldung Sleuth Tutorial,9cdebbffe8bbbade,9cdebbffe8bbbade,false] 12516 
  --- [nio-8080-exec-6] c.b.spring.session.SleuthController      : New Span
2017-01-11 21:07:54.924  
  INFO [Baeldung Sleuth Tutorial,9cdebbffe8bbbade,9cdebbffe8bbbade,false] 12516 
  --- [nio-8080-exec-6] c.baeldung.spring.session.SleuthService  : 
  I'm in the original span
2017-01-11 21:07:55.924  
  INFO [Baeldung Sleuth Tutorial,9cdebbffe8bbbade,1e706f252a0ee9c2,false] 12516 
  --- [nio-8080-exec-6] c.baeldung.spring.session.SleuthService  : 
  I'm in the new span doing some cool work that needs its own span
2017-01-11 21:07:55.924  
  INFO [Baeldung Sleuth Tutorial,9cdebbffe8bbbade,9cdebbffe8bbbade,false] 12516 
  --- [nio-8080-exec-6] c.baeldung.spring.session.SleuthService  : 
  I'm in the original span

Мы видим, что третий журнал разделяет traceId с другими, но она имеет уникальную spanId . Это может быть использовано для поиска различных разделов в одном запросе на более мелкозернистый трассировки.

Теперь давайте посмотрим на Сыщик поддержка потоков.

3.4. Охватывая Runnables

Продемонстрировать возможности резьбы Сыщик Давайте сначала добавим класс конфигурации для настройки пула потоков:

@Configuration
public class ThreadConfig {

    @Autowired
    private BeanFactory beanFactory;

    @Bean
    public Executor executor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor
         = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(1);
        threadPoolTaskExecutor.setMaxPoolSize(1);
        threadPoolTaskExecutor.initialize();

        return new LazyTraceExecutor(beanFactory, threadPoolTaskExecutor);
    }
}

Здесь важно отметить использование ЛенивыйTraceExecutor . Этот класс происходит от Сыщик библиотека и является особым видом исполнителя, который будет распространять наши traceId s к новым потокам и создавать новые spanId s в процессе.

Теперь давайте проведем этого исполнителя в наш контроллер и используем его в новом методе отображения запросов:

@Autowired
private Executor executor;
    
    @GetMapping("/new-thread")
    public String helloSleuthNewThread() {
        logger.info("New Thread");
        Runnable runnable = () -> {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            logger.info("I'm inside the new thread - with a new span");
        };
        executor.execute(runnable);

        logger.info("I'm done - with the original span");
        return "success";
}

С нашим runnable на месте, давайте перезагрузим наше приложение и перейти к “http://localhost:8080/new-thread”. Следите за выходом журнала, который выглядит так:

2017-01-11 21:18:15.949  
  INFO [Baeldung Sleuth Tutorial,96076a78343c364d,96076a78343c364d,false] 12516 
  --- [nio-8080-exec-9] c.b.spring.session.SleuthController      : New Thread
2017-01-11 21:18:15.950  
  INFO [Baeldung Sleuth Tutorial,96076a78343c364d,96076a78343c364d,false] 12516 
  --- [nio-8080-exec-9] c.b.spring.session.SleuthController      : 
  I'm done - with the original span
2017-01-11 21:18:16.953  
  INFO [Baeldung Sleuth Tutorial,96076a78343c364d,e3b6a68013ddfeea,false] 12516 
  --- [lTaskExecutor-1] c.b.spring.session.SleuthController      : 
  I'm inside the new thread - with a new span

Как и в предыдущем примере, мы видим, что все журналы имеют одинаковую traceId . Но журнал, поступающий из runnable имеет уникальный диапазон, который будет отслеживать работу, проделанную в этом потоке. Помните, что это происходит из-за ЛенивыйTraceExecutor , Если бы мы использовали нормального исполнителя, мы бы продолжали видеть то же самое spanId используется в новом потоке.

Теперь давайте рассмотрим Сыщик поддержка @Async методика.

3.5. @Async поддержка

Чтобы добавить поддержку async, давайте сначала изменим наш ThreadConfig класс для включения этой функции:

@Configuration
@EnableAsync
public class ThreadConfig extends AsyncConfigurerSupport {
    
    //...
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(1);
        threadPoolTaskExecutor.setMaxPoolSize(1);
        threadPoolTaskExecutor.initialize();

        return new LazyTraceExecutor(beanFactory, threadPoolTaskExecutor);
    }
}

Обратите внимание, что мы AsyncConfigurerSupport указать нашего исполнителя async и использовать ЛенивыйTraceExecutor для обеспечения правильного распространения traceIds и spanIds. Мы также добавили @EnableAsync на вершину нашего класса.

Давайте добавим метод async к нашему сервису:

@Async
public void asyncMethod() {
    logger.info("Start Async Method");
    Thread.sleep(1000L);
    logger.info("End Async Method");
}

Теперь давайте вызов в этот метод от нашего контроллера:

@GetMapping("/async")
public String helloSleuthAsync() {
    logger.info("Before Async Method Call");
    sleuthService.asyncMethod();
    logger.info("After Async Method Call");
    
    return "success";
}

Наконец, давайте перезапустим наш сервис и перейдите на “http://localhost:8080/async”. Следите за выходом журнала, который выглядит так:

2017-01-11 21:30:40.621  
  INFO [Baeldung Sleuth Tutorial,c187f81915377fff,c187f81915377fff,false] 10072 
  --- [nio-8080-exec-2] c.b.spring.session.SleuthController      : 
  Before Async Method Call
2017-01-11 21:30:40.622  
  INFO [Baeldung Sleuth Tutorial,c187f81915377fff,c187f81915377fff,false] 10072 
  --- [nio-8080-exec-2] c.b.spring.session.SleuthController      : 
  After Async Method Call
2017-01-11 21:30:40.622  
  INFO [Baeldung Sleuth Tutorial,c187f81915377fff,8a9f3f097dca6a9e,false] 10072 
  --- [lTaskExecutor-1] c.baeldung.spring.session.SleuthService  : 
  Start Async Method
2017-01-11 21:30:41.622  
  INFO [Baeldung Sleuth Tutorial,c187f81915377fff,8a9f3f097dca6a9e,false] 10072 
  --- [lTaskExecutor-1] c.baeldung.spring.session.SleuthService  : 
  End Async Method

Мы видим здесь, что так же, как наш насможимый пример, Сыщик распространяет traceId в метод async и добавляет уникальный spanId.

Давайте теперь рассмотрим пример с использованием весенней поддержки запланированных задач.

3.6. @Scheduled поддержка

Наконец, давайте посмотрим, как Сыщик работает с @Scheduled методика. Для этого давайте обновим наши ThreadConfig класс, позволяющий планировать:

@Configuration
@EnableAsync
@EnableScheduling
public class ThreadConfig extends AsyncConfigurerSupport
  implements SchedulingConfigurer {
 
    //...
    
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.setScheduler(schedulingExecutor());
    }

    @Bean(destroyMethod = "shutdown")
    public Executor schedulingExecutor() {
        return Executors.newScheduledThreadPool(1);
    }
}

Обратите внимание, что мы внедрили ПланированиеКонфигурера интерфейса и переопределен его configureTasks метод. Мы также добавили @EnableScheduling на вершину нашего класса.

Далее давайте добавим службу для наших запланированных задач:

@Service
public class SchedulingService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
 
    @Autowired
    private SleuthService sleuthService;

    @Scheduled(fixedDelay = 30000)
    public void scheduledWork() throws InterruptedException {
        logger.info("Start some work from the scheduled task");
        sleuthService.asyncMethod();
        logger.info("End work from scheduled task");
    }
}

В этом классе мы создали одну запланированную задачу с фиксированной задержкой в 30 секунд.

Давайте перезапустим наше приложение и подождем выполнения нашей задачи. Следите за консолью для вывода, как это:

2017-01-11 21:30:58.866  
  INFO [Baeldung Sleuth Tutorial,3605f5deaea28df2,3605f5deaea28df2,false] 10072 
  --- [pool-1-thread-1] c.b.spring.session.SchedulingService     : 
  Start some work from the scheduled task
2017-01-11 21:30:58.866  
  INFO [Baeldung Sleuth Tutorial,3605f5deaea28df2,3605f5deaea28df2,false] 10072 
  --- [pool-1-thread-1] c.b.spring.session.SchedulingService     : 
  End work from scheduled task

Мы видим здесь, что Сыщик создал новый след и пролет идентификаторов для нашей задачи. Каждый экземпляр задачи получит свой собственный след и пролет по умолчанию.

4. Заключение

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

К настоящему времени мы можем видеть, как Весенний облачный сыщик может помочь нам сохранить наше здравомыслие при отладке многотемной среды. Путем определения каждой операции в traceId и каждый шаг в spanId мы действительно можем начать ломать наш анализ сложных заданий в наших журналах.

Даже если мы не пойдем в облако, Весенний сыщик скорее всего, критическая зависимость практически в любом проекте; это бесшовные для интеграции и является массовым добавлением стоимости .

Отсюда вы можете исследовать другие особенности Сыщик . Он может поддерживать отслеживание в распределенных системах с РестТемплет , через протоколы обмена сообщениями, используемые КроликМЗ и Редис , и через шлюз, как Зул.

Как всегда, вы можете найти исходный код более на Github .