Принцип и практика агента цепочки вызовов vivo

Java

1. Предыстория проекта

В 2017 году команда vivo Internet R&D считала, что система цепочки вызовов имеет большое значение для реального бизнеса, поэтому они приступили к исследованиям и разработкам. За последние три года общая структура системы цепочки вызовов продолжала развиваться... В этой статье будут представлены технические принципы и практический опыт работы агента системы цепочки вызовов vivo.

Исследования и разработка системы цепочки вызовов vivo начались с Google.Dapper, a Large-Scale Distributed Systems Tracing Infrastructure«При изучении этой классической статьи мы исследовали связанные системы в отрасли: EagleEye, систему отслеживания распределенных услуг (SGM), платформу мониторинга приложений в реальном времени (CAT),Zipkin,PinPoint,SkyWalking, Бо Жуй и др. Путем исследований и анализа мы сосредоточились на изучении того, как SkyWalking зарыл точки. Далее я шаг за шагом представлю некоторые ключевые технологии, используемые в Агенте.

Во-вторых, введение цепочки вызовов

1. Общая структура

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

2. Основные концепции предметной области

В цепочке вызовов есть два основных понятия, а именно трассировка и охват, оба из которых происходят из оригинального введения Google в dapper.Будь то продукты цепочки вызовов отечественных производителей или реализация цепочек вызовов с открытым исходным кодом, модель предметной области обычно опирается на это.Есть две концепции, поэтому, если вы хотите хорошо понять цепочку вызовов, сначала нужно иметь четкое представление об этих двух концепциях.

Изображение выше имитирует простой сценарий:

Запрос инициируется с мобильного телефона, и после маршрутизации на серверную часть он сначала перенаправляется nginx для обработки в сервис А. Сервис А сначала запрашивает данные из базы данных, а затем продолжает инициировать запрос к сервису Б после простого После завершения обработки служба B возвращает результат A., и, наконец, мобильный терминал успешно получает ответ, и весь процесс обрабатывается синхронно.

В сочетании с смоделированным сценарием выше я даю определение:

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

Мы используем traceId, чтобы пометить конкретный вызов запроса.Конечно, traceId является распределенным и уникальным.Он последовательно соединяет всю ссылку.Правила генерации traceId будут введены позже. Обратите внимание, что вызов запроса той же бизнес-логики можно понимать как тот же интерфейс, что и запись для инициирования вызова. Из-за структуры ветвления, такой как if/else в логике программы, определенный вызов не может полностью отражать трассировочную ссылку. Только ссылка, достигнутая несколькими вызовами запросов одной и той же бизнес-логики, может рассматриваться как полная трассировочная ссылка после синтеза.

Span: локальный вызов запроса.

Вызов будет генерировать несколько интервалов, и эти интервалы образуют неполную трассировку; диапазон должен помечать вызывающую ссылку вызова (то есть информацию traceId в данных диапазона) и уровень ссылки, на которой он расположен; spanId находится на одном уровне Атомарное самоинкрементирование, кросс-уровень будет соединять «.» и подпоследовательности; например, span 1.1 и 1.2 на приведенном выше рисунке принадлежат одному уровню, span 1 и 1.1 или 1.2 являются кросс-уровневыми ; связь между B и D представляет собой вызов rpc, этот процесс состоит из 4 шагов: B инициирует вызов, затем D получает запрос, затем D возвращает результат B, а затем B получает ответ от D. Эти 4 шага составляют полный диапазон, поэтому B и D имеют только половину этого диапазона, поэтому spanId необходимо передавать между процессами, и как его передать, будет представлено позже.

3. Базовая логика сбора данных в цепочке вызовов

Позиционирование системы цепочки вызовов vivo — это мониторинг сервисного уровня, который является важной частью системы интернет-мониторинга vivo. Такие как исключения службы, трудоемкие вызовы RPC, медленный SQL и т. д. — все это основные точки мониторинга. Если данные, собранные скрытой точкой, должны соответствовать длительному мониторингу вызовов, то, по крайней мере, в сценариях вызова RPC и медленного мониторинга SQL сбор данных скрытой точки будет реализован в форме АОП. В дополнение к прямому использованию java.lang.management.ManagementFactory для сбора индексов JVM агент цепочки вызовов vivo реализован в форме АОП. Ниже приведен псевдокод:

beginDataCollection(BizRequest req);
try{
    runBusiness();// 业务代码执行
}catch(Throwable t){
    recordBizRunError(Throwable t);
    throw t;
}finally{
    endDataCollection(BizResponse resp);
}

3. Основные технические принципы

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

1. Правила генерации распределенного идентификатора (traceId)

traceId в цепочке вызовов играет очень важную роль.В предыдущей главе упоминалось, что он используется для объединения спанов, разбросанных по нескольким процессам.Кроме того, контроль выборки на стороне агента, идентификация записи от него зависит ключ для вычисления внутренней метрики flink, ссылка на полный вызов пользовательского запроса, конкатенация глобального бизнес-журнала и хеширование данных Kafka, HBase и ES. Системная цепочка вызовов vivo traceId представляет собой строку длиной 30. На рисунке ниже у меня есть цветные сегменты со специальными значениями.

  • 0e34:

PID системы Linux в шестнадцатеричном формате используется для различения нескольких процессов на одном компьютере, чтобы нельзя было повторить traceId разных процессов на одном компьютере.

  • c0a80001:

Шестнадцатеричное представление ipv4 может идентифицировать IP-адрес машины, сгенерировавшей этот traceId. Например, процесс шестнадцатеричного представления 127.0.0.1 выглядит следующим образом: 127.0.0.1->127 0 0 1->7f 00 00 01.

  • из:

Представляет внутреннюю бизнес-операционную среду vivo. Как правило, мы будем различать офлайн- и онлайн-среды, а офлайн-среды можно разделить на разработку, тестирование, стресс-тестирование и т. д., и это d представляет собой онлайн-среду.

  • 1603075418361:

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

  • 0001:

Атомарный самоувеличивающийся идентификатор в основном используется для повышения уникальности распределенных идентификаторов Текущий дизайн может выдерживать 10000 * 1000 = 10 миллионов одновременных операций в секунду для одной машины.

2. Полноканальная передача данных

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

Студенты, изучающие общие исследования и разработки в области Java, знают, что инструментальный класс ThreadLocal в JDK используется для изоляции безопасности данных в многопоточных сценариях и используется чаще, но мало кто использовал InheritableThreadLocal, существующий в JDK 1.2, а я никогда им не пользовался.

InheritableThreadLocal используется для копирования данных из ThreadLocalMap в дочерний поток при создании потока с помощью new Thread(), но обычно мы редко используем метод new Thread() для непосредственного создания потока, а InheritableThreadLocal бессилен в сценарии с пулом потоков. . Вы можете себе представить, что после пересечения межпотокового или межпотокового пула важные данные, такие как traceId и spanId, будут потеряны и не могут быть переданы позже, что приведет к отключению ссылки вызова запроса, который не может быть подключен через traceId и система цепочки вызовов сказали, что это тяжелый удар. Так что эта проблема должна быть решена.

На самом деле легко передавать данные между процессами, например, для HTTP-запросов мы можем поместить данные в заголовок http-запроса, вызов Dubbo можно поместить в RpcContext для последующей передачи, а сценарий MQ может размещать в заголовке сообщения. Передача данных между пулами потоков не может быть ненавязчивой для бизнес-кода.Агент цепочки вызовов vivo реализуется путем перехвата загрузки ThreadPoolExecutor и изменения байт-кода пула потоков ThreadPoolExecutor с помощью инструментов байт-кода.Это также, как правило, с открытым исходным кодом.Вызов Цепная система не имеет возможности.

3. Введение в javaagent

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

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

  • Во-первых, агент использует технологию javaagent, так что бизнес-сторона может получить доступ без вторжения и восприятия;

  • Во-вторых, стабильность Агента была признана бизнес-направлением в Интернете.С начала проекта в 2017 году до конца 2019 года была проведена только одна проверка сбоя на стороне бизнеса, связанная с ним.

Однако поначалу все было не так гладко: в начале модуль спрятанной точки агента должен был вторгнуться в бизнес-логику, а первая версия похоронила SpringMVC и Dubbo, требуя от пользователей настройки mvc filter и dubbo filter в коде, что крайне неэффективно., я до сих пор благодарен брату, который сотрудничал с бизнес-линейкой первой версии триала. Позже мы решительно изменили решение javaagent, сейчас я представлю технологию javaagent.

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

Для разработки приложений javaagent вам необходимо овладеть следующими знаниями:

  • использование параметра javaagent;

  • Понимать механизм инструментирования JDK (метод premain, интерфейс ClassFileTransformer) и настройку параметров Premain-Class в файле MANIFEST.MF;

  • использование инструментов байт-кода;

  • Принцип и применение технологии изоляции классов нагрузки.

Ниже я объясню один за другим:

(1) Пример конфигурации javaagent выглядит следующим образом:

java -javaagent:/test/path/my-agent.jar myApp.jar

JAR (здесь my-agent.jar), настроенный параметром javaagent, загружается с помощью AppClassLoader, который будет представлен в последующих главах.

(2) Так называемый механизм Instrumentation относится к замене классов байт-кодом через два интерфейса java.lang.instrument.Instrumentation и java.lang.instrument.ClassFileTransformer в jdk.Конечно, запись логики замены должна перехватить загрузку классов. В Java jar есть стандартный файл конфигурации META-INF/MANIFEST.MF, и в файл можно добавить k-v конфигурацию. Здесь нам нужно настроить k — это Premain-Class, v — это полностью квалифицированный класс Java, этот класс Java должен иметь метод public static void premain (String agentOps, Instrumention instr). Таким образом, когда вы используете команду Java для запуска исполняемого файла jar, этот метод будет выполнен. Нам нужно завершить регистрацию логики преобразования байт-кода в этом методе. При совпадении определенного класса логика преобразования байт-кода будет выполнено. , введите свою скрытую логику.

(3) Конфигурация в файле MANIFEST.M

Параметр Can-Retransform-Classes на рисунке означает, разрешено ли jvm выполнять логику преобразования.Вы можете прочитать JavaDoc в классе Instrumentation, чтобы углубить свое понимание. Параметр Boot-Class-Path используется для указания того, что классы в следующем банке загружаются с помощью BootstapClassLoader.

(4) Что касается использования инструментов байт-кода, агент цепочки вызовов vivo использует следующие операции:

  • Изменить логику указанного метода (внедрение АОП-подобной логики);

  • Добавьте поля экземпляра в класс;

  • Сделать класс реализующим определенный интерфейс;

  • Получить поля экземпляра класса и значения статических полей, прочитать родительские классы и интерфейсы и выполнить другие операции чтения.

4. Структура данных базовой модели

Выше мы упомянули, что span означает локальный вызов.Этот вызов будет генерировать данные половины диапазона на обеих сторонах вызова службы.Определение половины диапазона в памяти (определение на конец 2017 года) выглядит следующим образом:

public class Span {
    final transient AtomicInteger nextId = new AtomicInteger(0);//用于同一层级的spanId自增
    String traceId;
    String spanId;
    long start;
    long end;
    SpanKind type;//Client,Server,Consumer,Producer
    Component component;//DUBBO,HTTP,REDIS......
    ResponseStatus status = ResponseStatus.SUCCESS;
    int size;//调用结果大小
 
    Endpoint endpoint;//记录ip、port、http接口、redis命令
    List<Annotation> annotations;//记录事件,比如sql、未捕获异常、异常日志
    Map<String, String> tags;//记录标签tag
}

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

5. Подробная информация о скрытых точках каждого компонента

Здесь я перечислил компоненты, охватываемые скрытой точкой агента в цепочке вызовов vivo к концу 2019 года, и конкретное местоположение скрытой точки. Понятно, что после того, как в этом году система цепочки вызовов vivo вошла в версию 3.0, было добавлено более 8 скрытых точечных компонентов, а собранных данных становится все больше и больше.

6. Введение возможности полуавтоматического закапывания

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

  • Отладьте основной процесс выполнения логики стороннего фреймворка/компонента, который необходимо скрыть, поймите процесс его выполнения и выберите соответствующую точку входа логики AOP.Выбор точки входа должен быть простым для получения данных каждого поля в пролете;

  • Создайте класс аспекта скрытой точки, наследуйте определенный родительский класс, реализуйте абстрактный метод, отметьте метод, который будет врезан в скрытую точку в методе, и перехватчик, используемый для реализации логики AOP;

  • Реализовать логику перехватчика, получить некоторые данные в методе openSpan и завершить сбор оставшихся данных в методе closeSpan;

  • Класс, который устанавливает/управляет логикой перехватчика, может быть загружен загрузчиком классов Thread.currentThread().getContextClassLoader(), а затем включает переключатель, чтобы встроенная логика этого компонента вступила в силу.

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

7. Диаграмма потока данных диапазона

Давайте посмотрим на полный жизненный цикл span от генерации до отправки в kafka.

Как видно из рисунка, после создания полного (завершенного вызова closeSpan()) полудиапазона (обратитесь к подразделу концепции основной области введения цепочки вызовов) он сначала будет кэширован в пространстве ThreadLocal. После завершения всей логической обработки этого потока выполните finish() для сброса в дисраптор, а затем потребительский поток дисраптора регулярно сбрасывает в клиентский кеш kafka и, наконец, отправляет его в очередь kafka.

При выполнении внутреннего шаринга здесь задавались два вопроса: один — у самого клиента kafka есть кеш, зачем в середине дисраптор, а второй — время выполнения финиша. Причина здесь тоже очень проста: во-первых, потому что разрушитель неблокирующий и неблокирующий, а емкость очереди может быть ограничена, потокобезопасность в jdk либо заблокирована, либо не может ограничить начальную емкость. kafka client явно не соответствует этому условию. , мы не должны блокировать выполнение бизнес-потока. Вторая проблема может быть решена с помощью структуры данных стека (LinkedList).Когда поток выполняется до первой скрытой точки и выполняет openSpan, он будет толкать стек и выполнять closeSpan при извлечении из стека. нет данных в стеке, должен быть выполнен Finish.

8. Богатые стратегии внутреннего управления

В начале проекта основными целями были доступ к услугам и применимость возможностей продукта.Внутреннее управление не рассматривалось слишком много.Однако после того, как объем данных станет большим, необходимо больше внимания уделять собственному управлению, как показано на рисунке выше.К концу 2018 года были определены основные возможности внутреннего управления Агента. Далее я представлю предысторию каждой возможности управления одну за другой.

**(1)**Настроить трансляцию:

Возможность доставки является краеугольным камнем других возможностей управления.При выполнении метода premain агент переходит в центр конфигурации vivo, чтобы активно получать конфигурацию.Если конфигурация центра конфигурации изменяется, он также активно отправляет конфигурацию вниз. Кроме того, во многих местах агент зависит от конфигурации, а внутренняя конфигурация вступает в силу на основе механизма мониторинга Observer в JDK для обеспечения распределения конфигурации.

**(2)**Политика журнала:

В 2017 году интернет-бизнес vivo был на подъеме, а возможности единого центра журналов были слабыми.Большое количество аномальных журналов могло повлиять на центр журналов, поэтому требовалось управление нештатным потоком. Сократите печать ненормальных стеков в ненормальных условиях, а агент также должен иметь возможность собирать бизнес-журналы определенного уровня в ответ на бизнес-требования.Журналы уровня собираются в систему цепочки вызовов для устранения неполадок. Кроме того, сам Агент должен распечатать лог, код для печати лога встраивается в трехсторонний фреймворк после расширения байткода, то есть при выполнении бизнес-логики в трехстороннем фреймворке выполнение может замедляться не работает, что повлияет на производительность бизнеса, поэтому необходимо выводить журналы асинхронно. Последний момент, который необходимо упомянуть, это то, что печать логов реализована в самом агенте, во избежание конфликтов классов с фреймворком логирования, используемым бизнес-стороной, нельзя использовать сторонний фреймворк логирования.

**(3)**Стратегия выборки:

В начале 2018 года, когда обращались менее чем к 200 сервисам, собранные данные о спанах уже занимали емкость 10 физических машин Kafka, и было необходимо управление потоком, а ключевым моментом была выборка. Однако исходная логика выборки принесет новые проблемы, то есть неточность бизнес-tps, поэтому данные, такие как tps, будут собираться позже самостоятельно.

**(4)**Понижение:

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

**(5)**Управление потоком исключений:

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

**(6)**Мониторинг потока полного цикла:

Агент будет отслеживать процесс потока диапазона для подсчета (генерация, постановка в очередь, удаление из очереди, ввод успеха/ошибки Kafka, потеря данных).При обнаружении потери данных вы можете увеличить емкость очереди без памяти или уменьшить Kafka интервал отправки, когда обнаруживается, что отправка Kafka не удалась, это означает, что есть проблема с сетью или очередью Kafka.

**(7)**Контроль частоты агрегации данных:

Предполагается, что за последние 18 лет объем исходных данных на более позднем этапе вырастет до 150 млрд в день. У системы цепочки вызовов недостаточно ресурсов для обработки такого большого объема данных. Поэтому мы быстро поняли, возможность сквозной агрегации данных на стороне агента и будет первоначально агрегироваться. Полученные данные передаются на flink для окончательного расчета, что снижает нагрузку на Kafka и кластеры больших данных.

**(8)**Выборка JVM и управление частотой отправки kafka:

Агент будет регулярно собирать показатели JVM, такие как gc, cpu, использование памяти JVM, количество потоков в каждом состоянии и т. д. После расчета flink на странице будет отображаться линейный график.Интервал сбора строго 5 с. контролировать объем данных, необходимо добиться динамического контроля интервала сбора. Кроме того, данные диапазона, сгенерированные на стороне агента, сначала кэшируются в свободной от блокировки очереди без памяти, а затем отправляются в Kafka пакетами через регулярные промежутки времени, чтобы учесть сигнал тревоги в реальном времени и потребление ЦП. со стороны агента частота по умолчанию составляет 200 мс, также поддерживается удаленное управление.

4. Гарантия стабильности агента

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

1. Весь процесс не блокирует бизнес-процесс

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

**(1)**Точка блокировки потока 1 - печать журнала:

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

**(2)**Точка блокировки потока 2 - скрытая логика:

  • **Мера 1: **span кэшируется в ThreadLocal при его создании и эффективно выгружается в разрушитель пакетами, избегая конкуренции за барьеры нескольких производителей разрушителей;

  • **Мера 2:** Отражение необходимо в процессе захоронения точек, но в отражении есть ямки (нажмите здесь, чтобы узнать об этом), проанализируйте исходный код логики отражения и исправьте положение, используемое отражением;

  • **Мера 3:** Если возможно, не используйте отражение, а используйте технологию байт-кода, чтобы позволить классу скрытых точек реализовать пользовательский интерфейс и получать данные экземпляра объекта, выполняя вызовы обычных методов.

  • **Мера 4.** В процессе дампа данных между ThreadLocal и разрушителем, а также между разрушителем и Kafka объединяйте большие объекты коллекции, чтобы избежать чрезмерного потребления памяти.

**(3)**Точка блокировки потока 3 — отправка данных диапазона:

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

2. Надежность

Рассмотрение и решение граничной проблемы во многом зависит от личного опыта и технических возможностей разработчиков.Ниже я перечислил несколько ключевых вопросов, которые также больше беспокоят бизнес-сторону.

(1) Что делать, если есть проблема с собственной логикой агента?

Если весь процесс try-catch и его собственное исключение являются ненормальными, в течение 2 минут печатается только один журнал исключений.

(2) Что делать, если блокировки бизнес-потока нельзя избежать вовремя?

Понижение, прямой выход из единого процесса отслеживания.

(3) Что делать, если бизнес слишком занят, а потребление ЦП велико?

  • Контроль выборки + контроль частоты + деградация;

  • напрямую отказаться от данных;

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

(4) Что делать, если потребляется слишком много памяти?

Строго ограничить количество объектов данных памяти;

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

(5) Что делать, если Кафка не может подключиться/отключиться?

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

V. Знакомство со сложными технологиями и ключевыми реализациями

Ниже кратко представлены некоторые ключевые и сложные технологии Агента. Одним из самых сложных для контроля является то, что классы в агенте должны контролироваться тем, какой загрузчик классов загружать, иначе вы будете мучительно сталкиваться с различными ClassNotFoundException.

1. Запустите процесс

Процесс запуска агента кажется простым, и он опубликован здесь, чтобы помочь внутренним студентам прочитать исходный код. Следует отметить, что в начале запуска в качестве входа используется метод premain, а класс, в котором находится этот метод, загружается AppClasssLoader. В процессе запуска необходимо контролировать, какие классы или модули в Агенте загружаются каким загрузчиком классов, а некоторые классы активно загружаются через пользовательский загрузчик классов.Соединение различных пространств выполнения логики загрузчика классов осуществляется через jdk Прокси режим (InvocationHandler) в решении решается, что будет введено позже.

2. Архитектура приложения микроядра

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

журнал: пользовательская реализация

  • Адаптироваться к среде, различному поведению в разных средах;

  • Адаптировать к slf4j;

  • Уровень журнала динамически контролируется;

  • Автоматически определяйте один и тот же журнал ошибок, чтобы не влиять на центр журнала.

Мониторинг: краеугольный камень надежности

  • Мониторинг полного жизненного цикла скрытых данных (генерация, постановка в очередь, удаление из очереди, успешный/неудачный вход в kafka, потребление очереди памяти, потеря данных);

  • Отслеживайте состояние задержки выборки JVM.

Функциональный блок управления политикой:

  • Широковещательные события изменения конфигурации на основе режима наблюдателя;

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

Функциональный блок управления преобразованием байт-кода:

  • Плагин улучшения компонентов (настраиваемый);

  • Усиленная логическая изоляция друг от друга;

  • Улучшите высокую инкапсуляцию логики и реализуйте конфигурацию;

  • Основной процесс имитирует систему наследования класса Spring и обладает сильной расширяемостью.

Функциональный блок управления потоком:

  • Приложение очень модульное;

  • Механизм SPI хорошо масштабируется.

Блок управления изоляцией класса:

  • Настройте несколько загрузчиков классов для загрузки классов в разных местах и ​​банках;

  • Совместимость с отношениями наследования между Tomcat и загрузчиком классов JDK и активно позволяет Tomcat или определенному загрузчику классов явно загружать классы JDK;

  • Вмешательство в модель родительского делегирования класса, управление загрузкой родительского класса или интерфейса определенного класса.

3. Стек основных технологий

Направление стрелки на рисунке означает, что сложность использования технологии сверху вниз увеличивается, а также увеличиваются затраты времени на исследование и настройку. Среди них технология Java-зонда — javaagent, представленный выше.Отчет о выборе и предыстория ByteBuddy представлены ниже.Разрушитель в основном занимает много времени, чтобы понять техническую основу, чтение исходного кода и настройку, которая также представлена ​​позже. Применение контроля загрузки классов было самой большой головной болью в начале проекта, я до сих пор помню отчаяние при работе с ClassNotFoundException в конце 2017. Это далеко не решается знанием того, как настроить загрузчик классов и родительское делегирование. В начале я купил несколько книг с соответствующими вводными знаниями для исследования.Даже если бы я нашел только менее 1 страницы в каталоге этой книги и это были только возможные связанные страницы, инвестиции в покупку книг стоили сотни долларов.

4. Загрузка классов и контроль изоляции

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

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

  • 4 основных возможности загрузки классов;

  • Логика загрузки и выполнения класса, в котором находится premain;

  • Модель родительского делегирования JDK, как реализовать собственный загрузчик и как изменить порядок загрузки;

  • Изучение всех загрузчиков классов в JDK. Вначале я сбился с пути, и мне не терпится изучить исходный код C++, который не может понять JVM;

  • Архитектура загрузки классов Tomcat, прочитайте соответствующую часть исходного кода;

  • Прыжки в пространстве выполнения загрузчика классов.

6. Отчет о частичном выборе

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

1. ByteBuddy, инструмент для работы с байт-кодом

Для обычных Java-программистов программирование байт-кода — самая мощная черная технология, на которой можно играть. Что такое программирование байт-кода? Я считаю, что вы должны иметь некоторое представление о библиотеках редактирования байт-кода, таких как javassist и asm.Когда мы занимаемся программированием байт-кода, мы обычно используем эти библиотеки для динамического изменения или генерации байт-кодов Java. Например, Dubbo использует javassist для динамической генерации байт-кода некоторых классов. Основная причина выбора ByteBuddy заключается в том, что в начале проекта упоминалась логика SkyWalking, а ByteBuddy использовался SkyWalking в то время. Если бы мне пришлось выбирать сейчас, я бы отдал приоритет Javassist.Преимущества и недостатки нескольких фреймворков перечислены ниже.

(1) Байтбадди

На основе инкапсуляции, выполненной ASM, использовались проекты с открытым исходным кодом: Hibernate, Jackson.

преимущество:

  • Очень удобно использовать в определенных сценариях;

  • Автор 17-летнего фреймворка очень активен, поддерживает практически все новые возможности последнего jdk;

  • Простое пользовательское расширение.

недостаток:

  • Определение модели предметной области сбивает с толку, конструкция диаграммы классов сложна, внутренние классы могут иметь глубину до 8 уровней, eclipse не может декомпилировать несколько классов, исходный код трудно отлаживать и читать, и он крайне недружелюбен к глубокому анализу. Более 3 слоев, представьте, как выглядит 8-слойный глубокий внутренний класс!

(2) АСМ

Проекты с открытым исходным кодом: компилятор Groovy/Kotlin, CGLIB, Spring.

преимущество:

  • Написанный код очень дикий, а программирование, ориентированное на байт-код, — черная технология на уровне языка Java;

  • Видение посвящено производительности и малости, всего 28 классов во всем коде.

недостаток:

  • Он сложнее в использовании и эффективность кодирования низкая;

  • Вам необходимо лучше понимать набор инструкций байт-кода языка Java, а также знать макет содержимого файла класса.

(3) Джавасист

Проекты с открытым исходным кодом: Dubbo, MyBatis.

преимущество:

  • Простой и быстрый в использовании;

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

  • Примеры официальной документации просты для понимания и очень богаты.

недостаток:

  • Встроенный компилятор имеет определенный разрыв с Javac, и сложно реализовать сложные функции и новые возможности нового jdk.

2. Круговая безблокировочная очередь Disruptor

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

** (1) Основные характеристики: ** Неблокировка, низкая задержка, высокое потребление.

(2) Сценарии использования:

  • Высококонкурентная, неблокирующая система с малой задержкой;

  • Сегментированная архитектура, управляемая событиями.

(3) Почему так быстро?

  • используются volatile и cas lock-free операции;

  • Заполнение строки кэша используется для предотвращения ложного совместного использования;

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

  • Быстрые операции с указателями, преобразование по модулю И (m % 2^n = m & ( 2^n - 1 )).

(4) Меры предосторожности при использовании:

Стратегия ожидания потребителя: всестороннее рассмотрение блокировки потока службы, потребления ЦП и потери данных.

7. Резюме

Сделать хорошую работу по исследованию и разработке системы цепочки вызовов, очевидно, трудная задача.Сложность заключается не только в решении технических проблем агента, но также в принятии решений и изучении возможностей продукта, как использовать наименьшие ресурсы для удовлетворения требований к продукту и, что более важно, не знать больших объемов данных. Разработка данных на Java требует больших вычислений данных в условиях ограниченных ресурсов.

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

Автор: Ши Чжэнсин