hazelcast что это простыми словами

Национальная библиотека им. Н. Э. Баумана
Bauman National Library

Персональные инструменты

Hazelcast

Hazelcast: Apache 2.0

Hazelcast IMDG представляет собой In-Memory Data Grid решение с открытым исходным кодом на основе Java. Hazelcast — также название компании, разрабатывающей продукт.

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

Содержание

Обзор

Hazelcast имеет открытый исходный код. В дополнение к распространению данных In-Memory, Hazelcast предоставляет удобный набор API для доступа к ЦП в вашем кластере для максимальной скорости обработки. Hazelcast разработан как легкое и простое в использовании решение. Поскольку Hazelcast поставляется в виде компактной библиотеки (JAR), и поскольку он не имеет внешних зависимостей, отличных от Java, он легко подключается к вашему программному решению и предоставляет распределенные структуры данных и распределенные вычислительные утилиты.

Рисунок 1 – Архитектура Hazelcast IMDG

Версии Hazelcast IMDG

Основные свойства

Hazelcast написан на Java без каких-либо других зависимостей. Он предоставляет тот же API из стандартного пакетаJava util, используя те же интерфейсы. Просто добавьте hazelcast.jar в свой путь к классам, и вы сможете быстро использовать кластеры JVM и начать создавать масштабируемые приложения.

В отличие от многих NoSQL-решений, Hazelcast является одноранговым. Нет разделения master-slave; нет единой точки отказа. Все участники хранят равные объемы данных и выполняют равные объемы обработки. Вы можете встраивать Hazelcast в существующее приложение или использовать его в режиме клиента и сервера, где ваше приложение является клиентом узлов Hazelcast.

Hazelcast предназначен для масштабирования до сотен и тысяч узлов. Просто добавьте новые узлы, и они автоматически обнаружат кластер и будут линейно увеличивать объем памяти и производительность обработки. Узлы поддерживают TCP-соединение между собой, и все коммуникации выполняются через этот уровень.

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

Топология Hazelcast

Кластер Hazelcast можно развернуть двумя способами: встроенным или клиент-серверным.

Встроенное развёртывание

Если у вас есть приложение, основным предназначением которого являются асинхронные или высокопроизводительные вычисления и выполнение большого количества задач, то предпочтительным является встроенное развертывание. Во встроенном развертывании узлы включают как данные приложения, так и данные и службы Hazelcast. Преимуществом встроенного развертывания является доступ к данным с низкой задержкой. Схематично принцип встроенного развёртывания представлен ниже на рисунке 2.

Рисунок 2 – Встроенное развёртывание

Клиент-серверное развёртывание

В развертывании «клиент / сервер» данные и службы Hazelcast централизованы в одном или нескольких узлах сервера, и к ним обращается приложение через клиентов. У вас может быть кластер узлов сервера, которые могут быть независимо созданы и масштабированы. Ваши клиенты общаются с этими узлами, чтобы добраться до данных и услуг Hazelcast на них. Схематично принцип клиент-серверного развёртывания представлен ниже на рисунке 3.

Рисунок 3 – Клиент-серверное развёртывание

Развертывание «клиент-сервер» имеет преимущества в более предсказуемой и надежной производительности Hazelcast, более легком выявлении причин проблем и, самое главное, лучшей масштабируемости. При необходимости масштабирования в этом типе развертывания просто добавьте дополнительные узлы сервера Hazelcast. Проблемы масштабируемости клиентов и серверов можно решать отдельно.

Если требуется доступ к данным с низкой задержкой, как во встроенном развертывании, а также преимущества масштабируемости развертывания «клиент-сервер», можно рассмотреть возможность определения близких кэшей для клиентов. Это позволяет хранить часто используемые данные в локальной памяти клиента. [Источник 3]

Организация хранения данных

Рисунок 4 – Разделы кластера с одним узлом и двумя узлами

Рисунок 5 – Разделы кластера с четырьмя узлами

В правой части рисунка 4 разделы с чёрным текстом — основные, а резервно скопированные разделы обозначены синим цветом. Первый узел имеет 135 основных разделов (черные), и каждый из этих разделов резервно скопирован во втором узле (синие). В то же время первый узел также имеет резервные копии основных разделов второго узла. По мере добавления новых узлов Hazelcast перемещает некоторые из основных и резервных разделов к новым узлам по одному, делая все узлы равными и избыточными. Благодаря последовательному алгоритму хэширования, только минимальное количество разделов будет перемещено для масштабирования Hazelcast. На рисунке 5 приведен пример распределения реплик секций в кластере Hazelcast с четырьмя узлами. [Источник 4]

Установка

В качестве альтернативы вы можете скачать и установить Hazelcast IMDG самостоятельно. Нужно выполнить следующее:

Пример использования

В примере ниже демонстрируется работа с Hazelcast IMDG, а именно: создание простого с помощью distributed map и queue. Запуск наше приложение дважды, чтобы иметь кластер с двумя членами (JVM); подключение к нашему кластеру из Java-приложения с помощью Hazelcast Native Java Client API.

Следующий код запускает первый узел Hazelcast и создает и использует map и queue клиентов.

Запустите класс GettingStarted во второй раз, чтобы создать второй узел.Узлы сформируют кластер, который будет похож на следующее:

Здесь можно увидеть размер кластера (size) и версию списка участников (ver). Версия memberlist будет увеличена, когда в кластере произойдут изменения, например, присоединится или удалится узел.

При его запуске клиент правильно подключается к кластеру и печатает размер map, т.е. 3. [Источник 6]

Источник

Руководство для начинающих по Hazelcast, часть 1

Вступление

Что такое Hazelcast?

Hazelcast — это распределенная база данных в памяти. Есть проекты по всему миру, использующие Hazelcast. Код с открытым исходным кодом под лицензией Apache 2.0.

Особенности

В Hazelcast уже встроено множество функций. Вот некоторые из них:

Работа с Hazelcast

Просто игра с Hazelcast и чтение научили меня принимать эти вещи.

Позвольте мне объяснить эти предположения:

Данные будут храниться в виде массива байтов

Данные будут идти по сети

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

Данные удалены

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

Если данные не находятся в памяти, они не существуют

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

Разработчики Java будут рады узнать, что контейнеры хранения данных Hazelcast, кроме одного, являются расширениями интерфейсов java.util.Collections. Например, IList следует тем же контрактам методов, что и java.util.List. Вот список различных типов хранения данных:

пример

Настроить

Для всех функций, которые содержит Hazelcast, начальные шаги установки действительно просты.

Источник

Исследуем и тестируем очереди от Hazelcast

Многие из нас слышали о Hazelcast. Это удобный продукт, который реализует различные распределенные объекты. В частности: key-value хранилища, очереди, блокировки и т.д. К нему в целом применяются утверждения о распределенности, масштабируемости, отказоустойчивости и другие положительные свойства.

Читайте также:  какой материал называется латунью

Так ли это применительно к его реализации очередей? Где границы их использования? Это мы и попытаемся выяснить.

Все тесты доступны на GitHub. На всех устанавливалось ограничение памяти JVM в 64mb для ускорения достижения цели, дамп памяти при ее переполнении (OOM) и принудительное убиение приложение в случае возникновения этой беды

И так, поехали. Используется последняя стабильная версия hazelcast — 3.2.3.
(в тестах приводятся примерные данные по измерению скорости и количества. Не публикуется и конфигурация тестовой машины. Данные достаточны для сравнения тестов между собой что и является целью)

Тест 1 — память не безмерна

В первом тесте мы будем использовать один узел hazelcast. Создаем очередь и добавляем туда элементы пока не упадем от нехватки памяти.

Результат ожидаем. Удалось записать 460 тысяч объектов со скоростью 0.026ms на один элемент. Эти данные нам будут полезны далее для сравнения.

Изучаем дамп памяти:

Видим большое количество объектов QueueItem. Этот внутренний объект создается для каждого элемента очереди. Содержит в себе уникальный идентификатор элемента и собственно сами данные (объект Data)

Тест 2 — подключаем хранилище

Для того чтобы не хранить данные в памяти и тем самым освободить ее, мы можем подключить к очереди хранилище. Для теста мы подготовили MockQueueStore, которое ничего не делает, но исправно изображает хранилище, теряя все посланные в него элементы. Укажем параметр «memory-limit=0» для того чтобы совсем исключить хранение данных в памяти (по умолчанию хранится 1000 элементов).

Наши ожидания в избавлении от OOM, но не тут то было. Удалось записать больше объектов — 980k, но мы все равно упали.

Смотрим дамп памяти:

Видно что объектов типа Data нет, но QueueItem по прежнему в памяти. Это первое наше открытие. Реализация очередей hazelcast (QueueContainer) не избавляется от вспомогательного объекта при наличии хранилища. Она всегда их хранит во внутренней очереди (LinkedList).

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

Тест 3 — подсмотренная фича с транзакциями

Читая исходники реализации была обнаружена еще одна фича или баг. Hazelcast позволяет нам манипулировать своими распределенными объектами в транзакции. Давайте посмотрим что будет если мы в третий тест добавим транзакции во время добавления элементов.

Получаем OOM примерно на 250k элементах очереди. Смотрим дамп:

Видим что присутствует хранение данные в памяти (Data) хотя у нас подключено хранилище. И вместо объектов QueueItem используются TxQueueItem. Это все следствие реализации. При использовании транзакций не производится сброс данных в хранилище. А так как объект TxQueueItem это наследник QueueItem с доп полями, потребляющий больше памяти, то мы получили даже меньше элементов до OMM чем в первом тесте.

Вывод — транзакции и хранилище для очередей не работают вместе.

Двинемся дальше. Посмотрим как работают очереди в кластере.

Тест 4 — просто два узла

Не используем никакие доп настройки очередей. Все по умолчанию. Без хранилища. Укладываем и читаем 100k элементов. Укладываем и читаем в узел, владеющий очередью. Последнее иногда важно для скорости доступа к данным. Дело в том, что в отличии от Map, реализация очередей не распределенная. Все элементы очереди живут на одном из узлов кластера — у владельца очереди. Если точнее — у владельца партиции к которой принадлежит очередь. Мы ожидаем, что доступ будет быстрый — как без кластера, так как взаимодействуем с владельцем.

В результате имеем следующую скорость:
INFO: add 100000 0.255ms

INFO: poll 100000 0.223ms

Скорость упала на порядок (по сравнению с 0.026ms в первом тесте). Дело в том, что по умолчанию для очереди используется один бэкап. И hazelcast при добавлении и чтении синхронизировал данные со вторым узлом.

Тест 4_1 — попробуем работать не с владельцем

А есть ли разница в скорости, если добавлять в очередь и читать не с владельца партиции?

Оказывается существенной разницы нет:
INFO: add 100000 0.215ms
INFO: poll 100000 0.201ms

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

Тест 5 — убьем владельца

INFO: add 100000 0.267ms
INFO: poll 100000 0.025ms

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

Тест 6 — выключаем бэкап

Посмотрим что будет если в конфигурации очереди отключить бэкап и как в предыдущем тесте — удалить владельца.

INFO: add 100000 0.022ms

Результат — большая скорость работы с владельцем очереди. Он не тратит ресурсы на бэкап. Но после его падения — вся очередь теряется.

Тест 7 — подключим хранилище к кластеру

Бэкапа у нас нет и совершенно справедливо мы потеряли все данные после гибели узла — владельца очереди. Давайте подключим хранилище к этой конфигурации и посмотрим, выживут ли данные? Хранилище подсунем чуть более умное, чтоб хранило и отдавало данные из памяти (MemoryQueueStore).

INFO: add 100000 0.023ms
INFO: poll 100000 0.018ms

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

Немного деталей про восстановление очереди на втором узле. В этом процессе сначала считываются все ключи из хранилища в память реализацией QueueContainer и определяется наибольшее значение из них для дальнейшей генерации новых. Заполняется внутренняя очередь на LinkedList сразу всеми элементами очереди, но без данных. Для того чтобы порядок элементов в очереди после восстановления из хранилища сохранился — хранилище должно выдавать их в правильном порядке в наборе (Set). Далее по необходимости подгружаются данные из хранилища. Подгрузка идет пачками. По умолчанию по 250 штук.

Некоторые выводы

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

PS: Путь исправления реализации QueueContainer оставлен за рамками этого документа. Надеюсь будут время и силы с этим тоже поделиться.

Источник

Что, если выкинуть все лишнее из базы в распределенный кэш – наш опыт использования Hazelcast

Так как базы данных Яндекс.Денег вынуждены хранить массу второстепенной и временной информации, однажды такое решение перестало быть оптимальным. Поэтому в инфраструктуре появился распределенный Data Grid с функциями in-memory базы данных на базе Hazelcast.

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

Читайте также:  google garena g co helppay usa списание что это

Зачем понадобилась In-Memory база

В Яндекс.Деньгах Hazelcast используется как in-memory база данных и, во вторую очередь, как распределенный кэш для Java-инфраструктуры. При проведении каждого платежа нужно где-то держать массу информации, которая после совершения транзакции уже не нужна, и она должна быть легко доступна. Мы называем такие данные контекстом сессии пользователя и относим к ним источник и способ перевода денег, признак перевода с карты, способ подтверждения перевода и т.п.

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

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

Из ключевых требований к искомому решению были:

Отказоустойчивость как на уровне одного дата-центра (ДЦ), так и между двумя имеющимися.

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

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

Высокая скорость чтения/записи по сравнению с обычной БД и небольшой overhead по памяти для хранения данных.

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

Репликация как внутри дата-центров, так и между ними.

Высокий uptime в работе и возможность конфигурации на лету.

Возможность выставления фиксированного срока жизни объектов – TTL.

Распределенное хранение (шардинг) и балансировка нагрузки на узлы кластера со стороны клиента.

Поддержка мониторинга состояния кластера и возможность тестирования на локальном компьютере.

Простота настройки и поддержания инфраструктуры, гибкость.

Кроме всего этого, было бы здорово получить в довесок распределенный механизм блокировок, интеграцию с приложениями, кэш на стороне клиента, поддержку протокола Memcache, а также клиенты для JVM, Java, REST, Node.js.

Большей части этих требований удовлетворяют следующие продукты:

Redis – не позволяет указывать max-idle-seconds для записей кэша, выполнять сложную репликацию и ограничивать объём памяти по отдельному типу объектов.

Ehcache big memory – обладает хорошими характеристиками, но предоставляет только платную лицензию.

Gridgain – тоже хорош, но репликация между ДЦ и внутри ДЦ есть только в платной версии.

Infinispan – вроде бы всем хорош, но достаточно сложен в настройке и не содержит коммерческой поддержки. Что еще печальнее, в сети нет информации о поведении в продакшене, а это увеличивает наши риски.

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

Кластер о 25 нодах

Так как для инфраструктуры Яндекс.Денег необходима локальная и геоизбыточность, мы включили в кластер Hazelcast ноды в двух дата-центрах, как изображено на рисунке ниже.


На схеме изображен кластер Hazelcast, распределенный между двумя удаленными ДЦ.

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

Блок network отвечает за настройку адресов серверов, которые будут образовывать кластер (в нашей инфраструктуре это отдельные диапазоны под два ДЦ). Partition-group содержит настройки групп партиций, между которыми осуществляется резервное копирование данных. Здесь тоже привязка к двум ДЦ для дублирования данных в обоих.

Что, если перегрузить Hazelcast в 80 раз

После настройки системы и некоторого наблюдения за ней я могу отметить высокую скорость чтения-записи, которая не меняется даже при повышенных нагрузках (данные хранятся в памяти). Но, как и любая другая распределенная система, Hazelcast чувствительна к пропускной способности и отклику сети. Hazelcast – это Java-приложение, а значит, требует тонкой настройки сборщика мусора (Garbage Collector), согласно профилю нагрузки.

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


На графике представлено среднее время выполнения операций вставки и получения данных в Hazelcast для одного из клиентов. Среднее время вставки данных составило 2.1 мс, а чтения – 1.6 мс. Эти цифры отражают общую производительность системы: отправка запроса, его выполнение в кластере, сетевое взаимодействие и десериализация ответа.

Но при общем положительном фоне есть несколько областей, на которые стоит обратить особое внимание. Например, мы столкнулись со следующими проблемами при использовании Hazelcast:

Развал кластера и Split Brain, чреватый простоями и нарушением SLA.

Ложные срабатывания политик эвикта данных, которые приводят к потере данных.

Загрузка данных без учета настроек IMap приводит к засорению хранилища.

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

Развал кластера и Split Brain

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

Приложение запускается Spring Boot, который реализует свой classLoader. А между тем самописный classLoader Spring Boot имеет один очень нехороший баг. В случае нештатной ситуации кластер отправляет своим нодам идентификатор исключения для обработки ситуации. Ноды получают сообщения с ошибками и пытаются десериализовать классы исключений. Загрузчик класса Spring Boot не успевает загружать классы при высокой нагрузке и выдает ошибку NoClassDefFoundError.

В конечном итоге кластер может развалиться, преобразовавшись в несколько более мелких самостоятельных «кластеров». У нас такое как раз и произошло под нагрузкой, а в логах были лишь NoClassDefFoundError классов самого Hazelcast. В качестве лечения перед запуском приложения пришлось принудительно распаковывать классы всех библиотек следующей командой:

Чтобы такого не происходило в будущем, просто отключили в spring boot его сборщик пакетов:

Использование стандартного Class Loader исключило ошибки загрузки классов при работе приложения, но потребовало написания кода по сборке пакета для установки.

Ложные срабатывания политик эвикта данных


Garbage Collector за работой.

Чтобы корректно удалять ненужную информацию, мы используем связку из параметров TTL и MaxIDLE () для ограничения времени жизни данных в этих коллекциях, а также ограничение размера хранимых данных на каждой ноде.

Политик ограничения коллекции по размеру (MaxSizePolicy) несколько:

PER_NODE: Максимальное число записей для каждой JVM.

PER_PARTITION: Максимальное число записей для одной партиции.

Читайте также:  с каким мужчиной лучше расстаться

USED_HEAP_SIZE: Максимальный размер памяти, который могут занять записи конкретной коллекции – сумма вычисленных размеров каждой записи.

USED_HEAP_PERCENTAGE: То же самое что USED_HEAP_SIZE, только в процентах.

FREE_HEAP_SIZE: Минимальный размер оставшейся выделенной JVM памяти, на основе данных самой JVM.

Изначально использовали FREE_HEAP_PERCENTAGE, но в итоге переключились на USED_HEAP_PERCENTAGE. Дело в том, что эти похожие по назначению политики работают совершенно по-разному:

FREE_HEAP_PERCENTAGE – начинает очищать данные в коллекциях при Runtime.getRuntime().freeMemory() менее установленного лимита. Допустим, я хочу начать паниковать и удалять данные если осталось менее 10% доступной приложению памяти. Тогда получится постоянное срабатывание этой политики под нагрузкой. И это нормально, потому что так работает Java-машина при выделении и освобождении памяти.

Что касается FREE_HEAP_PERCENTAGE, мы пробовали настраивать GC так, чтобы порог доступной памяти никогда не достигался, но в лучшем случае ничего не менялось. Либо возникали проблемы с OldGen и Stop-the-World.

Использовав USED_HEAP_PERCENTAGE, удалось полностью избавиться от проблем с преждевременным эвиктом данных из коллекций. Одной из особенностей работы эвикта является механизм отбора элементов на удаление (EvictionPolicy: LRU, RANDOM и т.д.). Нам нужен LRU (Last Recently Used), но, с его точки зрения, только что загруженные данные и ни разу не запрошенные данные имеют одинаковый вес, что нужно учитывать.

Загрузка данных без учета настроек IMap

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

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

Долго выполняются команды в момент изменения структуры кластера

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

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

В свою очередь, проводить операции без выставленного таймаута тоже не лучшая идея, ведь по умолчанию он составляет 60 секунд – хватит ли у клиента терпения? Всех этих проблем можно избежать при штатной перезагрузке ноды Hazelcast – достаточно на клиенте не использовать smartRouting, а перед остановкой ноды остановить все её клиенты.

Мониторинг кластера

У Hazelcast есть собственное средство мониторинга – Management Center, доступный в лицензии Enterprise. Но все метрики доступны по JMX и их можно собирать, например, с помощью Zabbix. Так в нашей сети и мониторится занимаемая приложением память и, при необходимости, любая другая доступная метрика.

Тем не менее Zabbix беден в части возможностей по составлению запросов, построению и оформлению графиков, поэтому в большей степени он годится как источник данных для Grafana. Для мониторинга размеров коллекций, hit rate, latency их значения пересылаются в Graphite из компонента, управляющего запуском ноды Hazelcast.

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

Перезапуск нод кластера

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

Каждое изменение настроек Hazelcast – настройку новых или существующих коллекций, мониторинг, выделение памяти приложению – выполняется в рамках процесса релиза кластера как компонента нашей системы.

Для автоматизации процесса перезапуска всех нод с новыми настройками написан скрипт, который на основе данных мониторинга ноды принимает решение о возможности ее перезапуска с новой версией. У Hazelcast есть PartitionService с информацией о состоянии партиций кластера, включая информацию о бэкапах всех данных, isLocalMemberSafe(). Этот флаг скрипт интерпретирует как признак возможности безопасного перезапуска ноды – все ее данные могут быть восстановлены из бэкапов других нод.

Это позволяет отключить Terminate (жесткое отключение) ноды при получении сигнала SEGTERM. Скрипт посылает SEGTERM ноде, контекст приложения закрывается с вызовом Graceful Shutdown.

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

Потеря половины нод кластера

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


При полном выключении одного ДЦ Hazelcast репартиционировал данные на оставшиеся ноды, а скорость работы возросла почти в 2 раза.

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

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

Распределенная in-memory база позволила организовать удобное и «красивое» хранение массы временной информации. Кроме того, архитектура получилась не только производительной, но и неплохо масштабируемой. Но я бы поостерегся советовать подобные распределенные системы всем подряд, так как они довольно сложны в поддержке, преимущества которой можно ощутить только на действительно большом потоке данных.

Кроме того, по результатам проекта мы научились не доверять решениям только на основе их популярности (привет Spring Boot), а также тщательно испытывать новый продукт перед внедрением. Но даже после всех описанных в статье настроек придется что-то докручивать и менять: например, мне еще предстоит познать «радость» обновления с Hazelcast 3.5.5 до свежей версии 3.8. Соль в том, что версии обратно несовместимы и потому острые ощущения гарантированы. Но об этом я расскажу как-нибудь в другой раз.

Источник

Сказочный портал