- PVSM.RU - https://www.pvsm.ru -
Мы сделали 2 подсистемы внутри Apache Ignite.
В статье расскажу про их архитектуру:
Всем привет! Меня зовут Николай Ижиков. Я работаю в Сбербанк Технологии над развитием Open Source продуктов.
Замечание 1: «сделано» означает, что эта функциональность доступна в master Apache Ignite [1], можно собрать и посмотреть как все работает. Функционал попадёт в релиз 2.8 Apache Ignite, который будет выпущен в начале 2020 года.
Замечание 2: Статья про архитектуру, не будет конкретных чисел, которые понадобятся для мониторинга Apache Ignite.
Apache Ignite [2] распределенная in-memory платформа. Предоставляет много API: для кеширования, распределённых вычислений, сервисов (построения микросервисной архитектуры), обеспечивает транзакционность.
Допустим, у вас есть система и она как-то работает в проде. К вам обязательно придут администраторы и спросят: "А как посмотреть что происходит внутри, как система себя ведёт? Мы хотим узнать о происходящих процессах, до того момента как они приведут к падению или изменению поведения, которое невозможно скрыть." По сути вопросы сводятся к следующим:
Для ответов на часть этих вопросов необходимо разработать подсистему мониторинга.
В случае распределенной системы мониторинг это:
Подсистема мониторинга нужна, с этим определились. Но, перед тем как разрабатывать мы должны понять, что у нас есть уже. Получилось следующее:
Картина идеального мира:
Необходимо уточнить как происходит работа в Open Source community над такими большими и объемными задачами. У нас есть процесс, который называется Ignite Enhancement Proposal (IEP) [3], автор формулирует предлагаемые изменения, мотивацию, API, фазы изменения.
Было предложено несколько важных и долгоиграющих решений по дизайну:
Ignite распределённая система: есть глобальное состояние, поддерживаемое между нодами. В текущей реализации есть глобальные метрики, которые отражают состояние всего кластера. С моей точки зрения, это неверный подход. Если метрика должна быть глобальной это означает, что Ignite должен обеспечивать консистентность подсчета этой метрики по всему кластеру. Что ведет к тому, что Ignite выполняет не свойственные для себя функции — правильный подсчет метрик и поддержание их в актуальном состоянии. Мое глубокое убеждение — метрики должны быть локальными. После их сбора в системе агрегации метрик (prometheus, zabbix) мы можем вычислить глобальное состояние.
В Ignite много подсистем. Также Ignite должен предоставлять информацию о своём окружении: jvm, диск, памяти, версия ОС и др. Каждая из подсистем Ignite предоставляет свой набор метрик. Есть подсистемы, внутри которых так же есть иерархия.
Пример: кеши — хочется смотреть метрики по каждому кешу отдельно.
Так появляется несколько уровней иерархии: кеши, data region'ы, сетевые сообщения от конкретной ноды и т.д. Иерархическая структура отображена в названии метрики, слева направо идут уровни иерархии, разделенные точкой.
Пример: io.dataregion.myregion.TotalAllocatedPages
— количество страниц выделенных для Data Region с именем "myregion".
В коде реализовано так:
Metric
, Gauge
— конкретное число которое вычисляется при работе ноды. MetricRegistry
— набор (группа, реестр) метрик.MetricExporterSpi
— экспортер метрик во внешний мир.ReadOnlyMetricRegistry
— интерфейс, который доступен для exporter’о-в. read only access + listeners.GridMetricManager
— управление всем хозяйством.Выделено несколько типов метрик:
Metric
— счётчик. Обновляется в процессе работы ноды.LongAddterMetric
, DoubleMetricImpl
, ObjectMetricImpl
.Gauge
— значение. Вычисляется в момент обращения, обертка над Supplier. Поддерживать актуальное состояние счетчика не всегда просто. Бывает, проще написать алгоритм, который вычисляет значение.LongGauge
, DoubleGauge
, ObjectGauge
.HistogramMetric
— интервалы + количество событий в интервале. Гистограмма считает количество событий, значение которых попало в определенный интервал.HitRateMetric
— количество событий за последние X мсек.MetricExporterSpi
— каждый из экспортеров работает независимо, по своему протоколу. Он выгружает в любую систему свои метрики, что обеспечивает универсальность и гибкость. Из коробки поддерживаются JMX, SQL, Log, OpenCensus [4].
GridMetricManager mmgr = ...;
MetricRegistry mreg = mmgr.registry("io.dataregion." + name);
LongAdderMetric replacedPages = mreg.longAdderMetric("PagesReplaced",
"Number of pages replaced from last restart.");
//....
replacedPages.increment();
Кажется, что на вопрос "Что происходит?" мы ответили.
Осталось всего лишь реализовать все нужные метрики.
При наличии опыта работы с реляционными БД все понимают идею — везде есть системные представления. Мы назвали сущность system view, но можем показывать ее не только с помощью SQL. Поддерживаются любые протоколы (из коробки еще JMX).
SystemView
— именованный список объектов (таблица). Примеры: кэши, сервисы, compute task'и, sql table и т. д.SystemViewExporterSpi
— экспортер во внешний мир.ReadOnlySystemViewRegistry
— интерфейс для exporter'ов. read only access + listeners. GridSystemViewManager
— управление всем хозяйствомОсобенности:
При экспорте нужно знать схему данных: столбцы и их типы. Эта информация есть в java-классе. Задача выгрузки схемы формулируется следующим образом:
Есть пара десятков POJO. Их структура известна, новых классов не будет. Нужен быстрый проход по свойствам. Хочется поддерживать порядок обхода. Вопрос: Можно ли придумать более быстрый способ чем reflection?
Я сделал кодогенерацию:
Что получилось:
Интерфейс(SystemViewRowAttributeWalker.java [5]), который умеет обходить свойства объекта для формирования схемы и для выгрузки данных. Реализации интерфейса генерируются [6] автоматически. Заведение system view становится очень простым. После заведения представление становится доступно всем экспортерам.
ctx.systemView().registerView(CACHES_VIEW, CACHES_VIEW_DESC,
new CacheViewWalker(),
registeredCaches.values(),
CacheView::new);
ctx.systemView().registerView(CACHE_GRPS_VIEW, CACHE_GRPS_VIEW_DESC,
new CacheGroupViewWalker(),
registeredCacheGrps.values(),
CacheGroupView::new);
Reflection тормозит — как не удивительно, но не для всех это очевидно. Для обсуждения на dev-list я написал JMH benchmark [7]. Для reflection он берет список методов из класса. В случае walker он вызывает конкретные методы. Walker работает в 4 раза быстрее.
Кажется, что на вопрос "Что внутри?" мы ответили.
Сейчас на dev-листе идет очень интересная дискуссия [9] на тему "А правильно ли мы сделали?", а также по процессу замены устаревшего API [10]
Презентация — https://www.highload.ru/moscow/2019/abstracts/6111 [11]
Видео:
Автор: Николай Ижиков
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/346146
Ссылки в тексте:
[1] master Apache Ignite: https://github.com/apache/ignite/
[2] Apache Ignite: https://ignite.apache.org/
[3] Ignite Enhancement Proposal (IEP): https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=112820392
[4] OpenCensus: https://opencensus.io/
[5] SystemViewRowAttributeWalker.java: https://github.com/apache/ignite/blob/master/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/SystemViewRowAttributeWalker.java
[6] генерируются: https://github.com/apache/ignite/blob/master/modules/codegen/src/main/java/org/apache/ignite/codegen/SystemViewRowAttributeWalkerGenerator.java
[7] JMH benchmark: https://gist.github.com/nizhikov/fa1f005047c5396d93e547a210d06a94
[8] исходники: https://github.com/apache/ignite
[9] дискуссия: http://apache-ignite-developers.2346864.n4.nabble.com/Internal-classes-are-exposed-in-public-API-tt45146.html
[10] замены устаревшего API: http://apache-ignite-developers.2346864.n4.nabble.com/DISCUSS-Public-API-deprecation-rules-tt45647.html
[11] https://www.highload.ru/moscow/2019/abstracts/6111: https://www.highload.ru/moscow/2019/abstracts/6111
[12] Источник: https://habr.com/ru/post/486288/?utm_source=habrahabr&utm_medium=rss&utm_campaign=486288
Нажмите здесь для печати.