Apache Ignite Zero Deployment: точно Zero?


    Мы — отдел развития технологий розничной сети. Однажды руководство поставило задачу ускорить объемные вычисления за счет использования Apache Ignite в связке с MSSQL, показало сайт с прекрасными иллюстрациями и примерами Java-кода. На сайте сразу понравился Zero Deployment, описание которого обещает чудеса: you don't have to manually deploy your Java or Scala code on each node in the grid and re-deploy it each time it changes. По ходу работы оказалось, что Zero Deployment обладает спецификой использования, особенностями которой я и хочу поделиться. Под катом размышления и подробности реализации.


    1. Постановка задачи


    Суть задачи в следующем. Есть справочник точек продаж SalesPoint и справочник товаров Sku (Stock Keeping Unit). Точка продаж имеет атрибут "типМагазина" со значениями "малый" и "большой". К каждой точке продаж подключается (загружается из СУБД) ассортимент (список товаров точки продаж) и подается информация о том, что с указанной даты указанный товар
    исключается из ассортимента или добавляется в ассортимент.


    Требуется организовать партиционированный кеш точек продаж и хранить в нем информацию о подключенных товарах на месяц вперед. Совместимость с боевой системой требует от клиентского узла Ignite загружать данные, вычислять агрегат вида (типМагазина, кодТовара, день, число_точек_продаж) и выгружать его обратно в СУБД.


    2. Изучение литературы


    Опыта пока что нет, так что начинаю плясать от печки. То есть с обзора публикаций.


    Статья 2016 года Знакомство с Apache Ignite: первые шаги содержит ссылку на документацию проекта Apache Ignite и заодно упрек в невнятности этой документации. Перечитал пару раз, ясность не наступает. Обращаюсь к официальному туториалу getting-started, который
    оптимистично обещает "You'll be up and running in a jiffy!". Разбираюсь с настройками переменных среды, смотрю два видео Apache Ignite Essentials, для моей конкретной задачи они оказались не очень полезны. Успешно запускаю Ignite из командной строки со стандартным файлом "example-ignite.xml", собираю первое приложение Compute Application с помощью Maven. Приложение работает и использует Zero Deployment, какая красота!


    Читаю дальше, а там пример сразу использует affinityKey (создан ранее через SQL-запрос), да еще и применяется загадочный BinaryObject:


    IgniteCache<BinaryObject, BinaryObject> people 
            = ignite.cache("Person").withKeepBinary(); 

    Почитал немного: бинарный формат — что-то вроде рефлексии, доступ к полям объекта по имени. Может читать значение поля без полной десериализации объекта (экономия памяти). Но зачем вместо Person используется BinaryObject, ведь есть Zero Deployment? Зачем IgniteCache<Key,Person> переводится в IgniteCache<BinaryObject, BinaryObject>? Пока неясно.


    Переделываю Compute Application под свой случай. Первичный ключ справочника точек продаж в MSSQL определен как [id] [int] NOT NULL, создаю кеш по аналогии


    IgniteCache<Integer, SalesPoint> salesPointCache=ignite.cache("spCache")

    В xml-конфиге указываю, что кеш партиционирован


    <bean class="org.apache.ignite.configuration.CacheConfiguration">
        <property name="name" value="spCache"/>
        <property name="cacheMode" value="PARTITIONED"/>
    </bean>

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


    Читаю туториал First Ignite Compute Application, делаю по аналогии. На каждом узле кластера запускаю IgniteRunnable(), примерно вот так:


      @Override
      public void run() {
        SalesPoint sp=salesPointCache.get(spId);
        sp.calculateSalesPointCount();
        ..
      }

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


    Запускаю два тестовых сервера CentOs, указываю ip-адреса в default-config.xml, выполняю на каждом


    ./bin/ignite.sh config/default-config.xml

    Оба узла Ignite запускаются и видят друг друга. Указываю нужные адреса в xml-конфиге клиентского приложения, оно запускается, добавляет третий узел в топологию и сразу же узлов становится снова два. В логе значится "ClassNotFoundException: model.SalesPoint" в строке


    SalesPoint sp=salesPointCache.get(spId);

    StackOverflow говорит, что причина ошибки — на серверах CentOs нет пользовательского класса SalesPoint. Приехали. Как же "you don't have to manually deploy your Java code on each node" и далее по тексту? Или "your Java code" — это не про SalesPoint?


    Вероятно я что-то упустил — снова начинаю искать, читать и снова искать. Через время возникает ощущение, что я прочитал по теме все, ничего нового уже нет. Пока искал, нашел несколько интересных замечаний.


    Valentin Kulichenko, Lead Architect at GridGain Systems, ответ на StackOverflow, апрель 2016:


    Model classes are not peer deployed, but you can use withKeepBinary() flag
    on the cache and query BinaryObjects. This way you will avoid deserialization
    on the server side and will not get ClassNotFoundException.

    Еще одно авторитетное мнение: Denis Magda, Director of product management, GridGain Systems.


    Статья на Хабре про микросервисы ссылается три статьи Denis Magda: Microservices Part I, Microservices Part II, Microservices Part III 2016-2017 годов. Во второй статье Denis предлагает стартовать узел кластера через MaintenanceServiceNodeStartup.jar. Можно также использовать запуск с xml-конфигурацией и командной строкой, но тогда нужно вручную положить пользовательские классы на каждый развертываемый узел кластера:


    That's it. Start (..)  node using MaintenanceServiceNodeStartup file or pass
    maintenance-service-node-config.xml to Apache Ignite's ignite.sh/bat scripts.
    If you prefer the latter then make sure to build a jar file that will contain
    all the classes from java/app/common and java/services/maintenance directories.
    The jar has to be added to the classpath of every node where the service
    might be deployed.

    Действительно, that's it. Вот он, оказывается, зачем, этот загадочный бинарный формат!


    3. SingleJar


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


    Делаю по образу и подобию, получаю единый файл jar, который запускает "data node" либо "client node" в зависимости от аргумента командной строки. Сборка запускается и работает. Zero Deployment побежден.


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


    4. Выводы


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


    По итогу проделанной работы сложилось впечатление, что Zero Deployment работает, но только на системном уровне. Примерно так: BinaryObject применяется, чтобы научить удаленные узлы кластера работать с пользовательскими классами; Zero Deployment — внутренний механизм
    самого Apache Ignite и распространяет по кластеру системные объекты.


    Надеюсь, мой опыт будет полезен новым пользователям Apache Ignite.

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 2

      0
      Спасибо за статью и за то, что разложили пошагово все проблемные места! Лукавить не буду, про все эти скользкие моменты было известно и задача по написанию пошагового туториала для архитекторов и разработчиков стояла давно. Руки не доходили. Но, мы недавно выпустили новую документацию (работает и для Ignite) и теперь постараемся приступить к zero deployment and binary objects туториала в ближайшее время:https://www.gridgain.com/docs/latest/getting-started/what-is-ignite

      Вы в итоге деплоити jar-файлы со всеми объектами на серверах и peer-class-loading (zero deployment) отключили?
        0
        Да, забыл упомянуть в тексте. Peer-class-loading в xml-конфиге я включал/выключал, но это ни на что не влияло.
        Способ деплоя не поменялся, «data node» либо «client node» в зависимости от аргумента командной строки. Потому что работает — не трожь, да и другого варианта перед глазами нет.
        Полная десериализация пользовательских классов на дата нодах сильно расходует память, пришлось разбираться с BinaryObject. Теперь дата ноды не используют классы модели, но запуск всех узлов единым jar-файлом — удобная штука, пока остается.

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

      Самое читаемое