Проследяване на услуги, OpenTracing и Jaeger

Проследяване на услуги, OpenTracing и Jaeger

Ние използваме микросервизна архитектура в нашите проекти. Когато възникнат затруднения в производителността, много време се изразходва за наблюдение и анализиране на регистрационни файлове. Когато регистрирате времената на отделните операции в лог файл, обикновено е трудно да разберете какво е довело до извикването на тези операции, да проследите последователността от действия или изместването на времето на една операция спрямо друга в различни услуги.

За да минимизираме ръчния труд, решихме да използваме един от инструментите за проследяване. За това как и защо можете да използвате проследяване и как го направихме, и ще бъдат обсъдени в тази статия.

Какви проблеми могат да бъдат решени с проследяване

  1. Открийте тесните места в производителността както в рамките на една услуга, така и в цялото дърво на изпълнение между всички участващи услуги. Например:
    • Много кратки последователни обаждания между услуги, например към геокодиране или към база данни.
    • Дълги I/O чакания, като мрежови трансфери или четене на диск.
    • Дълго анализиране на данни.
    • Дълги операции, изискващи процесор.
    • Секции от код, които не са необходими за получаване на крайния резултат и могат да бъдат премахнати или отложени.
  2. Ясно разберете в каква последователност какво се нарича и какво се случва, когато се извърши операцията.
    Проследяване на услуги, OpenTracing и Jaeger
    Вижда се, че например заявката е дошла до услугата WS -> услугата WS допълва данните чрез услугата R -> след това изпраща заявка до услугата V -> услугата V зарежда много данни от R услуга -> отиде на услугата P -> услугата P отиде отново на услугата R -> услуга V игнорира резултата и отиде на услуга J -> и едва след това върна отговора на услугата WS, като същевременно продължи да изчислява нещо друго в Фонът.
    Без такава следа или подробна документация за целия процес е много трудно да се разбере какво се случва, когато погледнете кода за първи път, а кодът е разпръснат в различни услуги и скрит зад куп кошчета и интерфейси.
  3. Събиране на информация за дървото на изпълнение за последващ отложен анализ. На всеки етап от изпълнението можете да добавите информация към проследяването, която е налична на този етап, и след това да разберете какви входни данни са довели до подобен сценарий. Например:
    • Потребителски идентификатор
    • права
    • Вид на избрания метод
    • Грешка в регистъра или изпълнението
  4. Превръщане на следите в подмножество от показатели и допълнителен анализ вече под формата на показатели.

Каква следа може да регистрира. Обхват

В трасирането има понятието span, това е аналог на един дневник, към конзолата. СПА центърът разполага с:

  • Име, обикновено името на метода, който е бил изпълнен
  • Името на услугата, в която е генериран диапазонът
  • Собствен уникален идентификатор
  • Някакъв вид мета информация под формата на ключ/стойност, която е била вписана в нея. Например параметри на метода или методът е приключил с грешка или не
  • Начален и краен час за този интервал
  • ID на родителския диапазон

Всеки span се изпраща до колектора на span, за да бъде съхранен в базата данни за по-късен преглед веднага щом завърши изпълнението си. В бъдеще можете да изградите дърво от всички участъци, като се свържете чрез родителски идентификатор. Когато анализирате, можете да намерите, например, всички обхвати в някоя услуга, която отне повече от известно време. Освен това, като отидете до определен участък, вижте цялото дърво над и под този участък.

Проследяване на услуги, OpenTracing и Jaeger

Opentrace, Jagger и как го внедрихме за нашите проекти

Има общ стандарт opentrace, който описва как и какво трябва да се събира, без да се обвързва чрез проследяване към конкретна реализация на който и да е език. Например в Java цялата работа със следи се извършва чрез общия API на Opentrace и под него може да се скрие например Jaeger или празна реализация по подразбиране, която не прави нищо.
Ние използваме Егерова тъкан като внедряване на Opentrace. Състои се от няколко компонента:

Проследяване на услуги, OpenTracing и Jaeger

  • Jaeger-agent е локален агент, който обикновено се инсталира на всяка машина и услугите се влизат в него на локалния порт по подразбиране. Ако няма агент, следите на всички услуги на тази машина обикновено са деактивирани
  • Jaeger-collector - всички агенти му изпращат събрани следи и той ги поставя в избраната база данни
  • Базата данни е тяхната предпочитана касандра, но ние използваме elasticsearch, има реализации за няколко други бази данни и внедряване в паметта, което не записва нищо на диска
  • Jaeger-query е услуга, която отива в базата данни и връща вече събрани следи за анализ
  • Jaeger-ui е уеб интерфейс за търсене и преглед на следи, той отива към jaeger-query

Проследяване на услуги, OpenTracing и Jaeger

Отделен компонент може да се нарече внедряване на opentrace jaeger за конкретни езици, чрез които span се изпращат до jaeger-agent.
Свързване на Jagger в Java се свежда до внедряване на интерфейса io.opentracing.Tracer, след което всички следи през него ще летят до истинския агент.

Проследяване на услуги, OpenTracing и Jaeger

Също така за пружинния компонент можете да свържете opentracing-spring-cloud-starter и изпълнение от Jaeger opentracing-spring-jaeger-cloud-starter което автоматично ще конфигурира проследяване за всичко, което преминава през тези компоненти, например http заявки към контролери, заявки към базата данни чрез jdbc и т.н.

Регистриране на следи в Java

Някъде на най-горното ниво трябва да се създаде първият Span, това може да стане автоматично, например от пружинния контролер, когато се получи заявка, или ръчно, ако няма такава. След това се предава през обхвата по-долу. Ако който и да е метод по-долу иска да добави Span, той взема текущия activeSpan от Scope, създава нов Span и казва, че неговият родител е полученият activeSpan, и прави новия Span активен. При извикване на външни услуги, текущият активен участък се предава на тях и тези услуги създават нови участъци по отношение на този участък.
Цялата работа минава през екземпляра на Tracer, можете да го получите чрез DI механизма или GlobalTracer.get () като глобална променлива, ако DI механизмът не работи. По подразбиране, ако tracer не е инициализиран, NoopTracer ще се върне, което не прави нищо.
Освен това текущият обхват се получава от проследяващия през ScopeManager, създава се нов обхват от текущия с обвързване на новия обхват и след това създаденият обхват се затваря, което затваря създадения обхват и връща предишния обхват към активното състояние. Обхватът е свързан с нишка, така че при многопоточно програмиране не трябва да забравяте да прехвърлите активния участък към друга нишка, за по-нататъшно активиране на обхвата на друга нишка по отношение на този участък.

io.opentracing.Tracer tracer = ...; // GlobalTracer.get()

void DoSmth () {
   try (Scope scope = tracer.buildSpan("DoSmth").startActive(true)) {
      ...
   }
}
void DoOther () {
    Span span = tracer.buildSpan("someWork").start();
    try (Scope scope = tracer.scopeManager().activate(span, false)) {
        // Do things.
    } catch(Exception ex) {
        Tags.ERROR.set(span, true);
        span.log(Map.of(Fields.EVENT, "error", Fields.ERROR_OBJECT, ex, Fields.MESSAGE, ex.getMessage()));
    } finally {
        span.finish();
    }
}

void DoAsync () {
    try (Scope scope = tracer.buildSpan("ServiceHandlerSpan").startActive(false)) {
        ...
        final Span span = scope.span();
        doAsyncWork(() -> {
            // STEP 2 ABOVE: reactivate the Span in the callback, passing true to
            // startActive() if/when the Span must be finished.
            try (Scope scope = tracer.scopeManager().activate(span, false)) {
                ...
            }
        });
    }
}

За многопоточно програмиране има също TracedExecutorService и подобни обвивки, които автоматично препращат текущия диапазон към нишката, когато се стартират асинхронни задачи:

private ExecutorService executor = new TracedExecutorService(
    Executors.newFixedThreadPool(10), GlobalTracer.get()
);

За външни http заявки има TracingHttpClient

HttpClient httpClient = new TracingHttpClientBuilder().build();

Проблеми, с които се сблъскахме

  • Тогава Beans и DI не винаги работят, ако трасиращият не се използва в услуга или компонент Автоматично окабеляване Tracer може да не работи и ще трябва да използвате GlobalTracer.get().
  • Анотациите не работят, ако не е компонент или услуга, или ако методът е извикан от съседен метод от същия клас. Трябва да сте внимателни, за да проверите какво работи и да използвате ръчно създаване на проследяване, ако @Traced не работи. Можете също така да прикачите допълнителен компилатор за java анотации, тогава те трябва да работят навсякъде.
  • В старата пролет и пролетно зареждане, автоматичната конфигурация на пролетния облак на opentraing не работи поради грешки в DI, тогава, ако искате следите в компонентите на пролетта да работят автоматично, можете да го направите по аналогия с github.com/opentracing-contrib/java-spring-jaeger/blob/master/opentracing-spring-jaeger-starter/src/main/java/io/opentracing/contrib/java/spring/jaeger/starter/JaegerAutoConfiguration.java
  • Опитайте с ресурси не работи в groovy, трябва да използвате try finally.
  • Всяка услуга трябва да има свое собствено spring.application.name, под което ще се записват следите. Какво означава отделно име за продажба и тест, за да не им пречим заедно.
  • Ако използвате GlobalTracer и tomcat, тогава всички услуги, работещи в този tomcat, имат един GlobalTracer, така че всички те ще имат едно и също име на услуга.
  • Когато добавяте следи към метод, трябва да сте сигурни, че той не се извиква много пъти в цикъл. Необходимо е да се добави една обща следа за всички разговори, което гарантира общото време за работа. В противен случай ще се създаде излишно натоварване.
  • Веднъж в jaeger-ui бяха направени твърде големи заявки за голям брой следи и тъй като не изчакаха отговор, го направиха отново. В резултат на това jaeger-query започна да изяжда много памет и да забавя еластичността. Помогнах чрез рестартиране на jaeger-query

Вземане на проби, съхраняване и преглед на следи

Има три вида следи за вземане на проби:

  1. Const, който изпраща и запазва всички следи.
  2. Вероятностна, която филтрира следи с определена вероятност.
  3. Ограничаване на скоростта, което ограничава броя на следите в секунда. Можете да конфигурирате тези настройки на клиента, или на jaeger-agent, или на колектора. Сега използваме const 1 в стека на оценителя, тъй като няма много заявки, но те отнемат много време. В бъдеще, ако това ще окаже прекомерно натоварване на системата, можете да го ограничите.

Ако използвате cassandra, тогава по подразбиране тя съхранява следи само за два дни. Ние използваме еластично търсене и следите се съхраняват за цялото време и не се изтриват. За всеки ден се създава отделен индекс, например jaeger-service-2019-03-04. В бъдеще трябва да конфигурирате автоматично почистване на стари следи.

За да видите следите, трябва:

  • Изберете услугата, по която искате да филтрирате следите, например tomcat7-default за услуга, която се изпълнява в tomcat и не може да има собствено име.
  • След това изберете операцията, интервала от време и минималното време за операция, например от 10 секунди, за да се предприемат само дълги изпълнения.
    Проследяване на услуги, OpenTracing и Jaeger
  • Отидете до една от следите и вижте какво се забавя там.
    Проследяване на услуги, OpenTracing и Jaeger

Освен това, ако е известен идентификационният номер на някаква заявка, тогава можете да намерите проследяване по този идентификационен номер чрез търсене в маркер, ако този идентификационен номер е регистриран в обхвата на проследяване.

Документация

Статии

видео

Източник: www.habr.com

Добавяне на нов коментар