Русский
Русский
English
Статистика
Реклама

Apache ignite

Как быстро загрузить большую таблицу в Apache Ignite через Key-Value API

06.11.2020 10:14:52 | Автор: admin

Некоторое время назад на горизонте возникла и начала набирать популярность платформа Apache Ignite. Вычисления in-memory это скорость, а значит, скорость должна быть обеспечена на всех этапах работы, особенно при загрузке данных.
Под катом находится описание способа быстрой загрузки данных из реляционной таблицы в распределенный кластер Apache Ignite. Описана предобработка SQL query result set на клиентском узле кластера и распределение данных по кластеру с помощью задания map-reduce. Описаны кеши и соответствующие реляционные таблицы, показано, как создать пользовательский объект из строки таблицы и как применить ComputeTaskAdapter для быстрого размещения созданных объектов. Весь код полностью можно увидеть в репозитории FastDataLoad.


История вопроса


Этот текст перевод на русский моего поста в In-Memory Computing Blog на сайте GridGain.
Итак, некая компания решает ускорить медленное приложение путем переноса вычислений в in-memory кластер. Исходные данные для вычислений находятся в MS SQL; результат вычислений нужно положить туда же. Кластер распределенный, поскольку данных много уже сейчас, производительность приложения на пределе и объем данных растет. Заданы жесткие ограничения по времени работы.
Прежде чем писать быстрый код для обработки данных, эти данные нужно быстро загрузить. Неистовый поиск в сети показывает явную нехватку примеров кода, которые можно масштабировать до таблиц размером десятки или сотни миллионов строк. Примеров, которые можно загрузить, скомпилировать и пройти по шагам в отладке. Это с одной стороны.
С другой стороны, документация Apache Ignite / GridGain, вебинары и митапы дают представление о внутреннем устройстве кластера. Методом проб и ошибок удается сделать загрузчик, учитывающий распределение данных по партициям. И когда в один прекрасный день начальство спрашивает "а сыграл ли твой козырный туз?", ответ да, все получилось. Полученный код кажется некой самоделкой с привкусом внутренней архитектуры, но работает с достаточной скоростью.


Данные для загрузки (World Database)


Поскольку данных много, мы будем хранить записи в партиционированном виде и использовать data collocation, чтобы логически связанные значения хранились на одном и том же узле кластера. В качестве источника данных мы будем использовать файл world.sql из дистрибутива Apache Ignite.
Разделим его на три CSV файла в предположении, что каждый из них это результат SQL запроса:



Рассмотрим загрузку записей countryCache из файла country.csv. Ключ countryCache трехсимвольное поле code, тип ключа String, значение объект Country, созданный из остальных полей (name, continent, region).



Наивная загрузка


Поскольку опыта нет, то пляшем от печки будем загружать так же, как в монолитное нераспределенное приложение. Будем создавать пользовательский объект Country для каждой строки таблицы и класть в кеш перед тем, как перейти к следующей строке. Для этого используем библиотеку org.h2.tools.Csv, которая умеет конвертировать файл CSV в java.sql.ResultSet. Эта библиотека уже присутствует на каждом узле Apache Ignite и загружать ее не надо, поскольку подсистема SQL построена на субд H2.


    // define countryCache    IgniteCache<String,Country> cache = ignite.cache("countryCache");    try (ResultSet rs = new Csv().read(csvFileName, null, null)) {     while (rs.next()) {      String code = rs.getString("Code");      String name = rs.getString("Name");      String continent = rs.getString("Continent");      Country country = new Country(code,name,continent);      cache.put(code,country);     }    }

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


Попартиционная загрузка


Основа кластера Apache Ignite распределенный кеш ключ-значение. Если объем хранимых данных велик, кеш создается в режиме PARTITIONED и каждая пара ключ-значение хранится в некоторой партиции (partition) на некотором узле кластера. Из соображений отказоустойчивости копия этой партиции может храниться еще и на другом узле; мы здесь для простоты будем считать, что копий нет. Чтобы определить расположение пары ключ-значение, кластер использует affinity function, которая определяет, в какой партиции будет находиться данная пара и на каком физическом узле кластера будет находиться эта партиция.
В нашем примере требуется обработать ResultSet на клиентском узле кластера и распределить данные по серверным узлам. Клиентский узел не хранит данные, поэтому распределение данных гарантированно будет происходить по сети. На рисунке показано взаимодействие клиентского узла с тремя серверными узлами.



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


  • для хранения предзагруженных данных создадим HashMap вида partition_number -> key -> Value
    Map<Integer, Map<String, Country>> result = new HashMap<>();
    
  • для каждой строки данных создадим ключ и с помощью affinity function определим его partition_number. Вместо cache.put() для каждой строки положим пару ключ-значение в раздел HashMap с номером partition_number
    try (ResultSet rs = new Csv().read(csvFileName, null, null)) { while (rs.next()) {  String code = rs.getString("Code");  String name = rs.getString("Name");  String continent = rs.getString("Continent");  Country country = new Country(code,name,continent);  result.computeIfAbsent(affinity.partition(key), k -> new HashMap<>()).put(code,country); }}
    

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


Compute Task, вид изнутри


Согласно документации, "ComputeTaskAdapter initiates the simplified, in-memory, map-reduce process". Клиентский узел кластера сначала создает задания ComputeJobAdapter и выполняет map определяет, на какой физический узел кластера отправится каждое задание. Затем результаты выполнения заданий возвращаются на клиентский узел и там выполняется reduce вычисление общего числа добавленных записей.


Задание для узла данных (RenewLocalCacheJob)


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


targetCache.putAll(addend);

После размещения RenewLocalCacheJob печатает partition_number и число добавленных записей.


Задание для клиентского узла (AbstractLoadTask)


Каждое задание загрузки (пакет loader) наследник AbstractLoadTask. Задания отличаются именами извлекаемых полей и типами создаваемых пользовательских объектов. Загруженные данные могут предназначаться для кешей с ключами различных типов (примитивных либо пользовательских), поэтому AbstractLoadTask определен с параметром TargetCacheKeyType. Соответственно и предзагружаемый HashMap определен как


    Map<Integer, Map<TargetCacheKeyType, BinaryObject>> result;

В нашем примере только у countryCache ключ имеет примитивный тип String. Остальные кеши в качестве ключа используют пользовательские объекты. AbstractLoadTask определяет тип ключа параметром TargetCacheKeyType, а значение кеша и вовсе BinaryObject. Это все потому, что составной ключ это пользовательский объект и работать с ним просто так на узлах данных не получается.


Почему BinaryObject вместо пользовательского объекта


Наша цель положить в память узла кластера некоторое количество пользовательских объектов. Мы помним, что узел этот работает не только в другой JVM, но и на другом хосте где-то в сети. На этом узле class definition пользовательских объектов недоступен, он находится в JAR-файле на клиентском узле. Если мы определим кеш с типом Country


    IgniteCache<String, Country> countryCache;

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


Есть два способа преодолеть эту трудность. Первый обеспечить наличие классов на classpath, сам по себе достаточной элегантный:


  • сделать JAR-файл с пользовательскими классами внутри;
  • положить этот файл на classpath на каждом узле кластера;
  • не забывать обновлять этот файл при каждом изменении любого из пользовательских классов;
  • после обновления файла не забывать перезагружать узел.

Второй способ использовать интерфейс BinaryObject для доступа к данным в их исходном (сериализованном) виде. В нашем примере это выглядит так:


  • Кеш определяется в бинарном виде
    IgniteCache<String, BinaryObject> countryCache;    
    
  • Сразу после создания пользовательский объект Country преобразуется в BinaryObject (см. код в LoadCountries.java)
    Country country = new Country(code, name, .. );    BinaryObject binCountry = node.binary().toBinary(country);    
    
  • Созданный бинарный объект помещается в предзагружаемый HashMap, который определяется с типом BinaryObject
    Map<Integer, Map<String, BinaryObject>> result
    

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


Практическая часть. Запуск узлов данных.


Мы будем запускать кластер Apache Ignite в минимально достаточной конфигурации: два узла данных и один клиентский узел.


Узлы данных


Запускаются почти что из коробки с единственным изменением в файле default-config.xml мы разрешаем передавать классы заданий по сети между узлами. Шаги для запуска узла данных:


  • Установить GridGain CE по инструкции Installing Using ZIP Archive. На странице загрузки важно выбрать версию 8.7.10, поскольку код в репозитории FastDataLoad сделан имеенно для нее, а кластер из узлов разных версий собрать не получится;
  • В папке {gridgain}\config открыть файл default-config.xml и добавить в него
    строки
    <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">    <property name="peerClassLoadingEnabled" value="true"/></bean>
    
  • Открыть окно командной строки, перейти в папку {gridgain}\bin и запустить узел командой ignite.bat. В процессе тестирования оба узла кластера могут размещаться на одном хосте; для разработки и запуска на бою надо использовать разные машины;
  • Открыть еще одно окно командной строки, повторить предыдущий шаг. Если в обоих окнах появилась строка наподобие вот этой, то все получилось
    [08:40:04] Topology snapshot [ver=2, locNode=d52b1db3, servers=2, clients=0, state=ACTIVE, CPUs=8, offheap=3.2GB, heap=2.0GB]
    

Важно. Если все же нужно загрузить последнюю версию, например 8.7.25, придется указать номер версии в файле pom.xml


    <gridgain.version>8.7.25</gridgain.version>

Иначе версии узлов данных и клиентского узла не совпадут и клиентский узел не сможет войти в кластер


class org.apache.ignite.spi.IgniteSpiException: Local node and remote node have different version numbers (node will not join, Ignite does not support rolling updates, so versions must be exactly the same) [locBuildVer=8.7.25, rmtBuildVer=8.7.10]

Клиентский узел


Вся работа выполняется клиентским приложением, которое содержит определение кешей, пользовательские объекты и логику map-reduce. Приложение это JAR-файл, который стартует клиентский узел кластера и запускает compute task для загрузки данных. Для демонстрации мы используем один хост Windows, для боевого запуска лучше использовать разные хосты Linux. Шаги для запуска клиентского узла:


  • Клонировать репозиторий FastDataLoad;
  • Перейти в корневой каталог проекта и собрать проект;
    mvn clean package
    
  • Находясь в корневом каталоге, запустить приложение.
    java -jar .\target\fastDataLoad.jar
    

Метод main() класса LoadApp создает пользовательский объект LoaderAgrument с названиями кеша и файла данных и преобразует его в бинарный формат. Далее бинарный объект используется как аргумент map-reduce задания LoadCountries.
LoadCountries создает для каждой партиции задание RenewLocalCacheJob и отправляет его по сети на соответствующий узел данных, где задание выполняется и выводит сообщение с номером обновленной партиции (номера партиций между узлами не пересекаются).


Узел данных #1



Узел данных #2



После этого клиентский узел суммирует возвращенные заданиями результаты и выводит общее число загруженных объектов



Файл country.csv загружен, ключи CountryCode и соответствующие значения собраны в партициях и каждая партиция размещена в памяти своего узла данных. Процесс повторяется для cityCache и countryLanguageCache; клиентское приложение выводит число объектов, время работы и завершается.


Заключение


Пару слов о скорости работы наивной и попартиционной загрузки.


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


  • свойства загружаемой таблицы (SQL Server Management Studio):
    • число строк 44 686 837;
    • объем 1.071 GB;
  • время загрузки и предобработки данных на клиентском узле 0H:1M:35S;
  • время на создание заданий RenewLocalCacheJob и reduce результатов 0H:0M:9S.

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

Подробнее..
Категории: Nosql , Java , Apache , Apache ignite , High performance

Релиз Apache Ignite 2.9.0 что нового?

13.11.2020 14:18:57 | Автор: admin
Apache Ignite это высокопроизводительная распределенная база данных с открытым исходным кодом, предназначенная для хранения и распределенной обработки больших объемов данных в кластере узлов. Мы в Сбере активно его используем, и у нас есть команда, занимающаяся разработкой этого продукта. 23 октября 2020 года вышла новая версия Apache Ignite 2.9.0. Как менеджер данного релиза от лица всей команды разработчиков Apache Ignite хочу поделиться информацией об основных нововведениях.

  • Snapshots (Резервное копирование)
  • Трэйсинг
  • Новые возможности тонких клиентов
  • Режим работы кластера Только чтение
  • Запуск пользовательского кода в песочнице
  • Прозрачное шифрование данных: ротация мастер ключа
  • Инструменты для прерывания пользовательских задач и запросов
  • Кэширование на стороне платформы (.NET)
  • Подключение клиентских узлов к серверным через NAT


Snapshots (Резервное копирование)


В Ignite 2.9.0 появилась возможность создания резервной копии всех сохраняемых на диске кэшей (то есть кэшей, работающих в режиме Ignite Native Persistence) со всего кластера. Снапшоты могут создаваться онлайн, на активном кластере с пользовательской нагрузкой. При этом создается полностью консистентная копия всех данных кластера.

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

  • с помощью command-line утилиты control.sh: control.sh --snapshot create <snapshot name>;
  • JMX операцией: MBean group="Snapshot", name=SnapshotMXBeanImpl, операция createSnapshot(<snapshot name>);
  • через Java API: Ignite.snapshot().createSnapshot("<snapshot name>").

Где <snapshot name> это уникальное имя снапшота.

После окончания формирования снапшота в директории work/snapshots/<snapshot name> (с настройками по умолчанию) каждого узла будет воссоздана структура файлового хранилища этого узла на момент старта снапшота. Сформированную файловую структуру можно использовать в дальнейшем для восстановления из резервной копии путем замены файлов с данными узла на файлы из директории снапшота.

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

Трэйсинг


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

В Ignite 2.9.0 трэйсинг охватывает следующие внутренние компоненты:

  • сообщения Discovery;
  • сообщения Communication;
  • процесс Exchange;
  • транзакции.

Чтобы посмотреть трэйсы, их необходимо экспортировать во внешнюю систему. Для этих целей Ignite использует библиотеку OpenCensus, которая из коробки предоставляет несколько экспортеров в различные системы (например, в Zipkin).

Ограничить объем экспортируемой информации можно, задав один или несколько перечисленных выше компонентов в качестве области интересов (scope) и установив частоту сэмплирования (настройки доступны для изменения в runtime).

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

Новые возможности тонких клиентов


В тонких клиентах java и .NET появился функционал Ignite, который до этого был доступен только в толстом клиенте.

Была добавлена возможность использовать:

  • cluster API & cluster group API (в .NET и java):
    • изменение режимов работы кластера;
    • получение информации о кластере;
    • фильтрация, группировка и получение информации об узлах кластера;
    • выполнение различных операций над группами узлов;
  • compute API (в .NET и java):
    • выполнение распределенных вычислений в кластере. В отличии от подобного функционала в толстом клиенте, который может использовать p2p class loader и сам автоматически загружать необходимые классы с клиента на серверные узлы, для запуска задачи тонким клиентом требуется чтобы весь исполняемый код уже был доступен в class-path серверных узлов (автоматическая загрузка классов с тонких клиентов не происходит);
  • Service Grid (пока только в java):
    • вызов сервисов Ignite. Как и в случае с compute API, тонким клиентом не предоставляется функционал по загрузке классов и развертыванию сервисов, возможен только вызов уже развернутых в кластере сервисов.

Кроме этого тонкий клиент .NET получил функцию автоматического обнаружения узлов кластера (Automatic Server Node Discovery), которая включается совместно с функционалом осведомленность о партициях (partition awareness). При использовании осведомленности о партициях клиент устанавливает соединение не с одним серверным узлом, а сразу с несколькими, для того чтобы по возможности отправить запрос на узел, который является основным для данных в этом запросе. Автоматическое обнаружение узлов кластера при этом позволяет не перечислять в конфигурации клиента все адреса узлов кластера. Достаточно чтобы клиент мог подключиться хотя бы к одному живому узлу, используя перечисленные в конфигурации адреса. Адреса остальных узлов клиент получит уже из кластера.

Подробнее об использовании новых возможностей можно узнать в соответствующих подразделах документации тонкого клиента java и тонкого клиента .NET.

Режим работы кластера Только чтение


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

С более подробной информацией о состояниях кластера вы можете ознакомиться в официальной документации.

Запуск пользовательского кода в песочнице


Ignite может запускать пользовательский код (такой как compute-задачи, слушатели событий, различные фильтры) на серверных узлах. Такой код выполнялся с теми же правами что и системный код Ignite и ему был доступен весь java API без ограничений. Потенциально опасный код мог нарушить работоспособность кластера (например, удалить файлы данных Ignite, завершить работу JVM и т.д.).

В версии 2.9.0 появилась возможность выполнения такого кода в песочнице с теми правами, которые были явно назначены субъекту доступа, запросившему исполнение этого кода (например клиентскому узлу). Права, назначенные субъекту доступа это коллекция объектов класса java.security.Permission, которые проверяются java перед выполнением некоторых действий.

Для функционирования Ignite Sandbox необходимо наличие двух установленных и включенных компонентов:

  • Java security manager. Отвечает за авторизацию субъектов при выполнении вызовов системных java-библиотек. По умолчанию отключен;
  • Ignite security processor. Отвечает за аутентификацию субъектов доступа. Из коробки с Ignite не поставляется, требуется самостоятельная реализация и подключение с помощью плагина.

С более подробной информацией об Ignite Sandbox вы можете ознакомиться в официальной документации.

Прозрачное шифрование данных: ротация мастер ключа


Прозрачное шифрование данных (TDE Transparent data encryption) функционал, позволяющий не хранить данные на диске в открытом виде. Шифрование данных на диске средствами СУБД требуется, например, для сертификации по стандарту безопасности данных PCI DSS. В Apache Ignite базовый функционал TDE (первая фаза) был реализован в версии 2.7. В текущей версии была реализована вторая фаза TDE ротация мастер-ключа (мастер-ключом зашифрованы хранящиеся на диске кэш-ключи). Третья фаза TDE (ротация кэш-ключей) будет реализована в следующем релизе.

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

Инструменты для прерывания пользовательских задач и запросов


В предыдущих версиях Ignite не было целостного механизма прерывания пользовательских задач и запросов администратором. У пользователей была возможность отмены своих задач и запросов. Для администраторов были доступны отдельные, никак друг с другом не коррелирующие, инструменты (например, можно было прервать транзакции списком, по фильтру, через JMX или утилиту control.sh, и убить SQL-запрос с помощью SQL-команды KILL QUERY). В текущем релизе у администратора появилась возможность прерывать

  • различные виды запросов (SQL, scan, continous),
  • транзакции,
  • сompute-задачи,
  • Ignite-сервисы,

используя унифицированный интерфейс.

Все эти виды задач и запросов могут быть прерваны любым из следующих способов:

  • утилитой control.sh;
  • через JMX;
  • SQL-командой.

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

Кэширование на стороне платформы (.NET)


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

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

Подключение клиентских узлов к серверным через NAT


В Ignite 2.9.0 появился режим сетевого взаимодействия, при котором соединения между толстым клиентом и сервером инициируются только на клиентской стороне (сервер не инициирует соединения к клиенту, но, при необходимости прямого взаимодействия с клиентом, просит клиента подключится к нему через уже установленные соединения клиента с другими серверами). Такой режим работы позволяет использовать конфигурации кластера, в которых между клиентскими и серверными узлами находится NAT (например, когда клиенты запущены в виртуальном окружении).

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

Заключение


Выше перечислены наиболее значимые изменения в релизе Apache Ignite 2.9.0. Но список изменений не ограничивается только ими. Как обычно, мы исправили множество ошибок и внесли множество других полезных улучшений. Полный список изменений можно посмотреть в release notes.
Подробнее..

Как совладать со сложностью распределённой системы. Мониторинг GridGain при помощи Control Center

27.10.2020 12:09:43 | Автор: admin

Представим, что вам нужно настроить мониторинг распределённой базы данных, такой как GridGain. Метрики положим в Prometheus. Графики нарисуем в Grafana. Про систему оповещения не забудем для этого настроим Zabbix. Для анализа трейсов воспользуемся Jaeger. Для управления состоянием и CLI хорош. А для SQL запросов воспользуемся бесплатным JDBC клиентом вроде DBeaver.


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


А теперь наймем для поддержки и настройки всего этого зверинца команду специалистов и будем жить долго и счастливо. Или нет, потому что столько ресурсов мы на эту задачу просто не получим. Придется искать способы удешевить обслуживание. В GridGain моя команда для этого написала собственный инструмент, закрывающий все потребности распределенной In-Memory платформы Apache Ignite или ее корпоративной версии GridGain.


Экран мониторинга GridGain Control Center


Control Center умеет работать с GridGain начиная с версии 8.7.23 любой редакции, а также с Apache Ignite версии не младше 2.8.1.


Что умеет наш велосипед (и почему нам не подошли готовые)


Control Center предоставляет единый интерфейс, интегрированный и разработанный специально для GridGain и максимально учитывающий его особенности. Методы визуализации подобраны под данные, поступающие из кластера. Управлять состоянием узлов, делать снимки данных, запускать SQL можно прямо из пользовательского интерфейса. Если что-то настроено неправильно, то сообщения об ошибках подскажут, какие шаги нужно предпринять.


Инструменты общего назначения, такие как Grafana, Prometheus, Zabbix и прочие, потребуют от вас долгих часов и дней чтения документации и испытания разных методов интеграции. Это гораздо более сложный путь, требующий терпения. При этом недостаточно разобраться лишь в одном продукте для реализации тех же возможностей нужно уметь пользоваться целой коллекцией инструментов. Даже при идеальной настройке эти инструменты не будут согласованы между собой, а команды управления придётся по-прежнему выполнять в терминале.


Давайте взглянем подробнее на то, с чем Control Center хорошо справляется.


Настройка


Перед командой разработки Control Center стояла задача сделать настройку кластера для мониторинга максимально простой. Для старта нужно скопировать модуль control-center-agent из директории libs/optional в libs в дистрибутиве GridGain. После старта узла в логах можно будет найти ссылку, пройдя по которой, вы попадёте на экран мониторинга на сайте control.gridgain.com. На этом настройка завершена.


Также Control Center можно запустить в собственном окружении и настроить кластер для мониторинга через него при помощи команды bin/management.sh --uri <URI>.


Для Apache Ignite первый шаг процедуры несколько отличается, так как агент не поставляется в дистрибутиве, но его можно скачать на сайте GridGain.


Процесс настройки подробно описан в документации.


Отображение метрик


Основой любой системы мониторинга является возможность работы с метриками. В Control Center этой функциональности уделено особое внимание. Экран Dashboard предлагает возможность настройки виджетов на основе метрик, поступающих из кластера. Среди доступных способов визуализации на данный момент есть график, гистограмма, таблица и тепловая карта (heat map). Также есть специфичные для GridGain виджеты, такие как список узлов или визуализация ребаланса.


Dashboard может содержать набор данных виджетов в любом удобном расположении друг относительно друга. Dashboard-ов может быть несколько, что позволяет создавать специализированные экраны мониторинга определённых подсистем GridGain. Например, экран мониторинга состояния региона данных может выглядеть следующим образом:


Экран мониторинга состояния региона данных


Настройка уведомлений


Метрики могут служить не только для визуального отображения информации. На основе них также можно настроить уведомления, которые будут срабатывать при удовлетворении указанных условий. Если вы волнуетесь, что память на одном из серверов переполнится, беспокойство можно облегчить, настроив уведомление по SMS или Email. Если соответствующая метрика пересечёт указанный порог и продержится на таком уровне достаточное время, то вам придёт об этом сообщение:


Сообщение от сервиса уведомлений

Трейсинг


Многие операции в кластере требуют взаимодействия между разными узлами. В случае неполадок или задержек бывает сложно определить, какой узел является виновником и на какой стадии возникла проблема.
Трейсинг одно из нововведений в GridGain и Apache Ignite, упрощающее анализ таких ситуаций. Это фреймворк, позволяющий отслеживать исполнение операций, даже если они имеют распределённый характер. Каждый этап исполнения (span) логируется и привязывается к операции, для которой данное отслеживание (trace) было запущено. Все транзакции в кластере могут быть отслежены, и для самых долгих из них есть возможность найти этап, занявший наибольшее количество времени. Хоть трейсинг и появился в GridGain совсем недавно, его поддержка уже реализована в Control Center.


Самая важная информация, относящаяся к трейсам, находится на видном месте, чтобы не было необходимости долго её искать:


Экран трейсинга


Подробные детали для каждого спана также доступны:


Детали спанов


Запуск SQL


У GridGain есть возможность работы с данными при помощи SQL. Для выполнения задач, связанных с SQL запросами, в Control Center есть отдельная секция. Там вы найдёте редактор кода с базовыми возможностями подсветки синтаксиса и автоподстановкой. Можно создать несколько вкладок с запросами, которые сохраняются между запусками:


SQL редактор


Запросы, выполняющиеся в текущий момент, можно найти на отдельной вкладке:


Запущенные запросы


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


Статистика запросов


Создание снимков данных


GridGain Ultimate Edition предлагает функционал снятия снимков данных (snapshots), позволяющий зафиксировать набор записей, присутствующий в кластере на момент начала создания снимка. При этом останавливать кластер не требуется. Для работы со снимками в Control Center есть отдельная секция, где вы можете создать новый снимок, а также применить, проверить или переместить существующий. Поддерживается работа с полными и инкрементальными снимками, со сжатием и без:


Список снимков данных


Управление кластерами


Такие административные действия, как активация и деактивация, изменение набора узлов в базовой топологии, настройка новых кластеров для мониторинга, собраны на отдельном экране Control Center. Здесь можно посмотреть на список своих кластеров или выполнить действия по настройке, для которых обычно требуется CLI. Это весьма полезный экран для администраторов, производящих работу над кластером, требующую ввода или вывода узлов из топологии:


Экран управления


Архитектура


Теперь рассмотрим, как Control Center устроен. Посмотрим на то, как происходит взаимодействие с кластером, как хранится служебная информация, и какие внутренние возможности GridGain используются в реализации Control Center.


Агент


Связующим звеном между Control Center и кластером выступает агент, функционирующий как часть каждого узла. Control Center Agent интегрирован с GridGain посредством API для плагинов. Если в classpath узла имеется соответствующий модуль, плагин инициализируется и устанавливает связь с Control Center. Взаимодействие между агентом и Control Center происходит по протоколу STOMP поверх Web Socket.


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


Как было раньше

Альтернативный вариант связи с кластером был реализован в другом инструменте мониторинга от GridGain Web Console, на смену которого пришёл Control Center. В Web Console агент работает в отдельном процессе и связывается с кластером посредством REST API. Такая схема несёт в себе трудности при настройке и поддержке дополнительного компонента, сложности с обеспечением его отказоустойчивости и безопасности проходящего через него трафика. В ответ на множественные отзывы пользователей, эта схема была заменена на более простую.


Добавление кластера к учётной записи


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


  • Cluster ID значение, генерируемое один раз за всё время работы кластера. Оно служит как уникальный идентификатор кластера во внутренних структурах Control Center. Каждый запрос в кластер или информация, поступающая из него, сопровождается данным идентификатором. Он является внутренней деталью реализации, но может быть замечен пользователем, например как часть URL в адресной строке браузера.


  • Cluster Secret значение, парное Cluster ID. Оно служит для подтверждения аутентичности кластера. Если злоумышленник каким-то образом получит доступ к чужому Cluster ID, то он не сможет подключить свой кластер с тем же идентификатором, так как у него не получится пройти проверку на соответствие Cluster ID и Cluster Secret.


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



При подключении кластера к Control Center происходит проверка соответствия его Cluster ID и Cluster Secret. Если проверка прошла успешно, то генерируется Connection Token. Информация о соответствии между Cluster ID и Cluster Secret, а также между Cluster ID и Connection Token хранится на стороне бэк-энда Control Center.


Ссылка, отображаемая в логах узлов кластера, содержит в себе Cluster ID и Connection Token. Кластер привязывается к учётной записи, которая была активна на момент открытия ссылки. Если пользователь не был аутентифицирован в Control Center, то будет создана временная учётная запись. Её можно превратить в постоянную, задав пароль и указав недостающие данные в настройках. Также токеном можно воспользоваться напрямую через пользовательский интерфейс без использования ссылки.


Метрики


В Apache Ignite и GridGain активно разрабатывается фреймворк для упрощения работы с метриками. Все метрики в системе регистрируются в единый реестр, из которого их можно экспортировать в удобном для конкретной цели формате. Подробности можно узнать в соответствующем IEP.


Интересной для Control Center является возможность реализации exporter-а метрик, который может быть использован для пересылки метрик из GridGain в стороннюю систему мониторинга. Control Center активно пользуется этим фреймворком и имеет свою реализацию exporter-а. Раз в установленный период времени агент собирает с кластера необходимые метрики и отправляет их в Control Center. Там они обрабатываются, сохраняются в базу и отправляются пользователям при наличии соответствующих браузерных сессий.


Трейсы


В GridGain и Apache Ignite находится в активной разработке функционал по сбору трейсов структурированной истории выполнения операций с указанием длительности каждого из этапов. На данный момент есть возможность сбора информации о работе следующих компонентов: Discovery SPI, Partition Map Exchange, Transactions, Communication. В будущем планируется покрытие и остальных компонентов.


Трейсинг GridGain совместим с OpenCensus API. Все трейсы и спаны регистрируются в фреймворке OpenCensus, после чего поступают в специальный компонент для экспорта информации о спанах SpanExporter. Как и в случае с метриками, Control Center имеет свою реализацию exporter-а, которая находится в агенте.


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


Хранение данных


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


Под капотом Control Center использует как интеграцию GridGain со Spring Data, так и Cache API напрямую. Инициализация состояния базы, генерация запросов и миграции реализованы при помощи механизмов Spring Data. В некоторых случаях базовых возможностей Spring Data оказалось недостаточно, поэтому интеграция была улучшена. Так, например, была добавлена поддержка Querydsl. Соответствующее улучшение в Apache Ignite на данный момент находится на этапе code review.


История метрик и трейсов хранится в GridGain только ограниченное время один день в случае метрик и неделю в случае трейсов. После этого данная информация удаляется согласно expiry policy, настроенной в GridGain.


Что будет дальше?


Несмотря на имеющиеся широкие возможности Control Center, активная разработка не останавливается. Всё ещё имеется ряд мест, которые предстоит разработать или улучшить.


Вот список самых значимых планируемых доработок:


  • Переработка механизма взаимодействия с фреймворком метрик. В скором будущем появится возможность задания шаблонов, по которым метрики будут отображаться на графиках. Сейчас для выбора доступен только преднастроенный набор шаблонов.
  • Интеграция отображения метрик и уведомлений о неполадках. Если для метрики настроено уведомление, это должно быть отражено на соответствующем графике.
  • Расширение набора поддерживаемых компонентов, доступных для трейсинга. Данная работа ведётся на стороне Core GridGain и Apache Ignite.
  • Интеграция с OpenID Connect и LDAP. Сейчас в Control Center доступна только нативная аутентификация, но в скором будущем появится возможность аутентификации через сторонние сервисы, такие как Google, Microsoft, Okta и т.д. Также планируется реализация механизма Single-Sign-On через OpenID Connect. Так, если в кластере настроена аутентификация через тот же сторонний сервис, что и для Control Center, повторная аутентификация в кластере не потребуется.
  • Поддержка GridGain Data Center Replication. Будут добавлены возможности по управлению и мониторингу процесса репликации между разными кластерами GridGain.

Стоило ли оно того?


Разработка собственного инструмента мониторинга серьёзная задача. В нашем случае это потребовало года интенсивной работы целой команды. Чего же мы этим добились? Повышения удобства использования GridGain нашего основного продукта. Разработав Control Center, мы решили задачу настройки инфраструктуры мониторинга для каждого нашего нового пользователя. Стоит ли делать такую систему мониторинга самому? У вас должны быть на это очень серьёзные причины. Для нас это была инвестиция, которая сделала весь наш продукт более удобным.


Ссылки и дополнительные материалы


Подробнее..

Архитектура транзакций в Apache Ignite

20.11.2020 10:22:07 | Автор: admin
В этой статье мы рассмотрим, как устроены транзакции в Apache Ignite. Не будем останавливаться на концепции Key-Value хранилища, а перейдем сразу к тому, как это реализовано в Ignite. Начнем с обзора архитектуры, а затем проиллюстрируем ключевые моменты логики транзакций при помощи трейсинга. На простых примерах вы увидите, как работают транзакции (и по каким причинам могут не работать).

Необходимое отступление: кластер в Apache Ignite


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



Данные, с логической точки зрения, принадлежат партициям, которые в соответствии с некоторой affinity-функцией распределены по узлам (подробнее о распределении данных в Ignite). У основных (primary) партиций могут быть копии (backups).



Как устроены транзакции в Apache Ignite


Архитектура кластера в Apache Ignite накладывает на механизм транзакций определенное требование: консистентность данных в распределенной среде. Это означает, что данные, находящиеся на разных узлах, должны изменяться целостно с точки зрения ACID принципов. Существует ряд протоколов, позволяющих реализовать требуемое. В Apache Ignite используется алгоритм на основе двухфазного коммита, который состоит из двух этапов:
  • prepare;
  • commit;

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

Рассмотрим, как происходят обе фазы, на примере следующей транзакции:
Transaction tx = client.transactions().txStart(PESSIMISTIC, READ_COMMITTED);client.cache(DEFAULT_CACHE_NAME).put(1, 1);tx.commit();

Prepare фаза


  1. Узел координатор транзакций (near node в терминах Apache Ignite) отправляет prepare-сообщение на узлы, содержащие primary-партиции для всех ключей, принимающих участие в данной транзакции.
  2. Узлы с primary-партициями отправляют Prepare-сообщение на соответствующие узлы с backup-партициями, если таковые имеются, и захватывают необходимые локи. В нашем примере backup-партиций две.
  3. Узлы с backup-партициями отправляют Acknowledge-сообщения на узлы с primary-патрициями, которые, в свою очередь, отправляют аналогичные сообщения на узел, координирующий транзакцию.


Commit фаза


После получения подтверждающих сообщений от всех узлов, содержащих primary-партиции, узел координатор транзакций отправляет Commit-сообщение, как показано на рисунке ниже.



Транзакция считается завершенной в тот момент, когда координатор транзакций получил все подтверждающие (Acknowledgment) сообщения.

От теории к практике


Чтобы рассмотреть логику работы транзакции, обратимся к трейсингу.
Для включения трейсинга в Apache Ignite необходимо выполнить следующие шаги:
  • Включим модуль ignite-opencensus и зададим OpenCensusTracingSpi как tracingSpi посредством конфигурации кластера:
    <bean class="org.apache.ignite.configuration.IgniteConfiguration">    <property name="tracingSpi">        <bean class="org.apache.ignite.spi.tracing.opencensus.OpenCensusTracingSpi"/>    </property></bean>
    

    или
    IgniteConfiguration cfg = new IgniteConfiguration();cfg.setTracingSpi(    new org.apache.ignite.spi.tracing.opencensus.OpenCensusTracingSpi());
    

  • Зададим некоторый отличный от нуля уровень сэмплирования транзакций:
    JVM_OPTS="-DIGNITE_ENABLE_EXPERIMENTAL_COMMAND=true" ./control.sh --tracing-configuration set --scope TX --sampling-rate 1
    

    или
    ignite.tracingConfiguration().set(            new TracingConfigurationCoordinates.Builder(Scope.TX).build(),            new TracingConfigurationParameters.Builder().                    withSamplingRate(SAMPLING_RATE_ALWAYS).build());
    

    Остановимся на нескольких моментах чуть подробнее:
    • Конфигурация трейсинга относится к классу экспериментальных API и потому требует включения флага
      JVM_OPTS="-DIGNITE_ENABLE_EXPERIMENTAL_COMMAND=true"
      
    • Мы задали sampling-rate равным единице, таким образом, сэмплировать будут все транзакции. Это оправдано для целей иллюстрации рассматриваемого материала, но не рекомендуется к использованию в продакшене.
    • Изменение параметров трейсинга, за исключением выставления SPI, имеет динамическую природу и не требует перезапуска узлов кластера. Ниже, в соответствующем разделе, доступные параметры настройки будут разобраны более подробно.

  • Запустим PESSIMISTIC, SERIALIZABLE транзакцию с клиентского узла на кластере из трех узлов.
    Transaction tx = client.transactions().txStart(PESSIMISTIC, SERIALIZABLE);client.cache(DEFAULT_CACHE_NAME).put(1, 1);tx.commit();
    


Обратимся к GridGain Control Center (подробный обзор инструмента) и взглянем на получившееся дерево спанов:



На иллюстрации мы видим, что корневой спан transaction, созданный в начале вызова transactions().txStart, порождает две условных группы спанов:
  1. Машинерию, связанную с захватом локов, инициированную put() операцией:
    1. transactions.near.enlist.write
    2. transactions.colocated.lock.map с подэтапами
  2. transactions.commit, созданный в момент вызова tx.commit(), который, как ранее упоминалось, состоит из двух фаз prepare и finish в терминах Apache Ignite (finish-фаза тождественна commit-фазе в классической терминологии двухфазного коммита).

Давайте теперь рассмотрим детально prepare-фазу транзакции, которая, начавшись на узле координаторе транзакций (near-узел в терминах Apache Ignite), продуцирует спан transactions.near.prepare.

Попав на primary-партицию, prepare-запрос триггерит создание transactions.dht.prepare спана, в рамках которого осуществляется отправка prepare-запросов на бекапы tx.process.prepare.req, где они обрабатываются tx.dht.process.prepare.response и отсылаются обратно на primary-партицию, которая отправляет подтверждающее сообщение на координатор транзакций, попутно создавая спан tx.near.process.prepare.response. Finish-фаза в рассматриваемом примере будет аналогична prepare-фазе, что избавляет нас от необходимости детального ее разбора.

Кликнув по любому из спанов, мы увидим соответствующую метаинформацию:



Так, например, для корневого спана transaction мы видим, что он был создан на клиентском узле 0eefd.

Мы также можем увеличить степень детализации трейсинга транзакций, включив трейсинг коммуникационного протокола.
Настройка параметров трейсинга
JVM_OPTS="-DIGNITE_ENABLE_EXPERIMENTAL_COMMAND=true" ./control.sh --tracing-configuration set --scope TX --included-scopes Communication --sampling-rate 1 --included-scopes COMMUNICATION

или
       ignite.tracingConfiguration().set(           new TracingConfigurationCoordinates.Builder(Scope.TX).build(),           new TracingConfigurationParameters.Builder().               withIncludedScopes(Collections.singleton(Scope.COMMUNICATION)).               withSamplingRate(SAMPLING_RATE_ALWAYS).build())




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

Обработка исключений и восстановление после сбоев


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

Выше мы говорили, что в контексте транзакций в Apache Ignite можно выделить три типа узлов:
  • Координатор транзакций (near node);
  • Узел с primary-партицией для соответствующего ключа (primary node);
  • Узлы с backup-партициями ключей (backup nodes);

и две фазы самой транзакции:
  • Prepare;
  • Finish;

Путем нехитрых вычислений получим необходимость обработки шести вариантов падений узла от падения бекапа на prepare-фазе до падения координатора транзакций на finish-фазе. Рассмотрим эти варианты подробнее.

Падение бекапа как на prepare, так и на finish-фазах


Такая ситуация не требует каких-либо дополнительных действий. Данные на новые backup-узлы доедут самостоятельно в рамках ребаланса с primary-узла.



Падение primary-узла на prepare-фазе


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



Падение primary-узла на finish-фазе


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



Падение координатора транзакций


Наиболее интересный случай потеря контекста транзакции. В такой ситуации primary- и backup-узлы непосредственно обмениваются локальным транзакционным контекстом друг с другом, тем самым восстанавливая глобальный контекст, что позволяет принять решение о верификации коммита. Если, например, один из узлов сообщит, что не получал Finish-сообщение, то произойдет откат транзакции.



Резюме


В приведенных примерах мы рассмотрели flow транзакций, проиллюстрировав его при помощи трейсинга, который в деталях показывает внутреннюю логику. Как видите, реализация транзакций в Apache Ignite близка к классической концепции двухфазного коммита с некоторыми ухищрениями в области производительности транзакций, связанными с механизмом взятия локов, особенностями восстановления после сбоев и логикой таймаута транзакций.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru