- PVSM.RU - https://www.pvsm.ru -
PHP + Java. Картинка взята отсюда [1]
В этом [2] комментарии к статье под названием «Пишите код каждый день» я сказал, что скоро покажу свой проект, на который я выделял ежедневно 1 час (кроме выходных). Так как в последнее время моя работа связана с написанием распределенных Java приложений, которые используют in-memory data grid (IMDG) в качестве хранилища данных, то мой проект связан именно с этим.
Подробнее про IMDG можно почитать в моих предыдущих статьях (1 [3], 2 [4]). Но если кратко, то это кластерное распределенное хранилище объектов по ключам, которое держит все данные в памяти, за счет чего достигается высокая скорость доступа к данным. Позволяет не только хранить, но и обрабатывать данные, не извлекая их из кластера.
И если интерфейс для обработки данных у каждого конкретного IMDG свой, то интерфейс доступа к данным обычно идентичен хеш-таблице.
Большинство IMDG написано на Java и поддерживают API для Java, C++, C#, при этом API для веб языков программирования (Python, Ruby, PHP) не поддерживается, а протокол для написания клиентов сильно ограничен. Именно этот факт я и считаю основным тормозом для проникновения IMDG в массы — отсутствие поддержки самых массовых языков.
Так как производители IMDG пока не предоставляют поддержку веб языков, то веб программисты не имеют возможностей по такому же легкому масштабированию приложений, какие есть у серверных Java разработчиков. Поэтому я решил сделать нечто подобное самостоятельно и выложить в open source, взяв в качестве движка open source IMDG JBoss Infinispan (компания JBoss, принадлежащая Red Hat, довольно хорошо известна в кругу java разработчиков). Мой проект называется Sproot Grid, пока доступен только для PHP, но если у сообщества будет интерес, то сделаю и интеграцию с Ruby и Python.
В этой статье я еще раз расскажу про in-memory data grid и про то, как конфигурировать, запускать и использовать Sproot Grid.
Самым узким местом многих высоконагруженных проектов является хранилище данных, в частности реляционная БД. Для борьбы с недостатками традиционных БД в основном используется 2 подхода:
1) Кэширование
плюсы:
минусы:
2) NoSQL решения
плюсы:
минусы:
IMDG объединяет достоинства обоих подходов и при этом имеет ряд преимуществ перед упомянутыми выше решениями:
Рассмотрим поближе эти 2 интересных механизма: read-through и write-behind (write-through)
Read-through — это механизм, который позволяет подтягивать данные из БД во время запроса.
Например вы хотите получить из кэша объект по ключу 'key', и при этом оказывается, что объекта с таким ключом в кластере нет, тогда автоматически этот объект будет прочитан из БД (или любого другого persistence storage), затем положен в кэш, после чего будет возвращен как ответ на запрос.
В случае отсутствия такого объекта в БД пользователю будет возвращен null.
Естественно, что необходимый sql-запрос, а также маппинг результатов запроса на объект лежит на плечах пользователя
Для оптимизации скорости записи вы можете писать не в БД, а напрямую в кэш. Звучит на первый взгляд странно, но на практике это хорошо разгружает БД и повышает скорость работы приложения.
Выглядит это примерно так:
Такая схема взаимодействия называется write-through. Она позволяет синхронизировать обновления с БД одновременно с обновлениями в кластере. Как можно заметить, такой подход не ускоряет процесс записи данных, но обеспечивает согласованность данных между кэшом и БД. Также при таком виде записи данные попадают в кэш, а значит доступ к ним на чтение всё равно будет выше, чем запрос к БД.
Если же одновременнаяя запись в БД не является критичным условием, тогда можно использовать более популярный механизм write-behind, он позволяет организовать отложенную запись в БД (любой другой сторадж). Примерно так:
При использовании write-behind операция записи существенно ускоряется, потому что пользователь не ждет, пока апдейт дойдет до БД, а просто кладет данные в кэш, а все апдейты одного и того же объекта будут слиты в один результирующий апдейт, при этом запись в БД происходит пачками, что тоже положительно сказывается на загрузке сервера БД,
Таким образом можно сконфигурировать свой IMDG так, чтоб каждые 3 секунды (либо 2 мин, либо 50 мс) все обновления данных асинхронно отправлялись в базу.
В первой версии я решил не реализовывать сразу всё, о чем рассказал выше, т.к. это отняло бы много времени, а мне хотелось бы побыстрее получить фидбэк от пользователей.
Итак, что доступно в Sproot Grid 1.0.0:
Сначала вам надо скачать дистрибутив отсюда [5] и распаковать его.
Так как JBoss Infinispan — это Java приложение, то необходимо было выбрать способ взаимодействия между Java и PHP. В качестве такого связующего звена был выбран Apache Thrift (протокол был разработан для сериализации и транспорта между узлами в Cassandra), поэтому для того, чтоб Sproot Grid мог работать на вашей системе необходимо установить следующее:
Инструкции по установке расположены на wiki проекта [7]
Файл конфигурации должен находиться в $deploymentFolder/sproot-grid/config/definition.xml, где deploymentFolder — это путь к директории, в которой вы распаковали дистрибутив
<?xml version="1.0" encoding="UTF-8"?>
<sproot-config>
<dataTypes>
<dataType type="somepackageUser" cache-name="user-cache">
<field name="id" type="integer" />
<field name="name" type="string" indexed="true" />
<field name="cars" type="array" key-type="string" value-type="somepackageCar"/>
</dataType>
<dataType type="somepackageCar" cache-name="car-cache">
<field name="model" type="string" />
<field name="isNew" type="boolean" />
</dataType>
<dataType type="string" cache-name="string-cache"/>
<dataType type="array" value-type="somepackageCar" cache-name="list-car-cache"/>
</dataTypes>
<cluster name="Sproot">
<multicast host="224.3.7.0" port="12345"/>
<caches>
<cache name="user-cache" backup-count="1">
<eviction max-objects="1000" lifespan="2000" max-idle-time="5000" wakeup-interval="10000" />
</cache>
<cache name="car-cache" backup-count="1" />
<cache name="string-cache" backup-count="1" />
<cache name="list-car-cache" backup-count="1" />
</caches>
<nodes>
<node id="1" role="service" thrift-port="34567" minThreads="5" maxThreads="100" />
<node id="2" role="storage-only" />
</nodes>
</cluster>
</sproot-config>
Подробнее о конфигурации можно почитать на wiki проекта [8]
Как можно заметить из конфигурации, для каждого типа объектов мы можем прописать имя кеша (а можем и не прописывать, если не хотим хранить такие объекты в отдельном кеше). Cache — это хеш-таблица, распределенная по кластеру, в кластере может быть сколько угодно кэшей. В одном кэше могут храниться только объекты одного и того же типа.
Все кеши должны быть описаны в секции <caches/>.
В конфигурации есть отдельная секция для описания структуры кластера и список кешей, которые будут в нем храниться.
<datatypes/> — описание типов, которые будут храниться в вашем кластере. Можно использовать как встроенные PHP типы, так и кастомные. Как можно заметить, для каждого типа объектов мы можем прописать имя кеша (а можем и не прописывать, если не хотим хранить такие объекты в отдельном кеше)
<cluster/> — описание структуры кластера и список кешей, которые будут в нем храниться.
<caches/> описывает кеши. Имя кеша должно быть уникальным, параметр backup-count определяет, сколько узлов кластера вы можете потерять без потери данных. Чем большее значение имеет backup-count, тем надежнее ваш кластер, но тем больше памяти он потребляет. Также можно сконфигурировать eviction (автоматическое удаление объектов из кеша), подробнее об этом на wiki страничке [8]
<multicast/> определяет мультикастовый адрес, который будет использоваться для сборки кластера. Как известно, для мультикаста доступны только сети класса D (224.0.0.0 — 239.255.255.255)
<nodes/> описывает количество и типы узлов кластера. Сейчас есть только 2 типа узлов: storage-only — занимается только хранением данных и выполнением внутренних запросов service — не только хранит данные, но и обрабатывает внешние запросы, поэтому для узлов данного типа необходимо указать порт, на котором будут приниматься запросы от PHP клиентов.
Для эффективной работы кластеру необходимо сгенерировать код, специфичный для вашего приложения (вашей доменной модели) и скомпилировать его Java часть, так как это работает быстрее, чем доступ к объектам через reflection. Чтобы сгенерировать и скомпилировать весь необходимый код, надо:
1) cd $deploymentFolder/sproot-grid/scripts
2) build.sh(or build.cmd)
, где $deploymentFolder — это тот каталог, в который вы распаковали дистрибутив
Генерацию кода необходимо производить только в случае изменения описания доменной модели, т.е. если ваша модель стабильна, то эту операцию вам придется произвести лишь один раз, после этого сгенеренные php исходники можно хранить в репозитории кода, а java часть будет скомпилирована в библиотеку. Т.е. не надо ничего генерить по 10 раз перед тем, как задеплоить ваше приложение, это делается только 1 раз на этапе разработки.
После окончания выполнения генерации кода, скопируйте папку с .php файлами из $deploymentFolder/sproot-grid/php/org в корень вашего приложения
1) cd $deploymentFolder/sproot-grid/scripts
2) run.sh(run.cmd) nodeId memorySize
, где nodeId — значение атрибута id секции в конфигурационном файле,
memorySize — количество памяти (в Мб или Гб), которые вы хотите выделить узлу
Например:
run.sh 1 256m
или
run.cmd 2 2g
На шаге генерации кода вы получили всё необходимое для интеграции с вашим приложением. Остальось только скопировать этот код в свое приложение, для этого скопируйте всё из папки $deploymentFolder/sproot-grid/php в корень своего приложения
Всё! Теперь можете использовать кластер из своего приложения.
<?php
require_once 'org/sproot_grid/SprootClient.php';
require_once 'some/package/User.php';
use orgsproot_gridSprootClient;
use somepackageUser;
$client = new SprootClient('localhost', 12345); // в качестве параметров в конструктор передаются хост и порт узла кластера типа 'service'
echo $client -> cacheSize('user-cache');
$user = new User();
$user->setName('SomeUser');
$user->setId(1234);
$client->put('user-cache', '1234', $user);
echo $client -> cacheSize('user-cache');
?>
Описание API можете найти здесь [9], но если вкратце, то API сейчас такой:
Sproot Grid опубликован под лицензией MIT.
Исходники [10]
Вики [11]
Дистрибутив [5]
Автор: gricom
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/open-source/60688
Ссылки в тексте:
[1] отсюда: http://obviousmag.org/en/archives/2011/12/the_art_that_lies_at_the_froth_of_your_cappuccino.html
[2] этом: http://habrahabr.ru/post/219209/#comment_7492403
[3] 1: http://habrahabr.ru/post/126580/
[4] 2: http://habrahabr.ru/post/126973/
[5] отсюда: https://bitbucket.org/aolenev/sproot/downloads
[6] Генерация кода: #StubGenerating
[7] на wiki проекта: https://bitbucket.org/aolenev/sproot/wiki/Getting%20Started
[8] на wiki проекта: https://bitbucket.org/aolenev/sproot/wiki/Configuration
[9] здесь: https://bitbucket.org/aolenev/sproot/wiki/API
[10] Исходники: https://bitbucket.org/aolenev/sproot
[11] Вики: https://bitbucket.org/aolenev/sproot/wiki/Home
[12] Источник: http://habrahabr.ru/post/197628/
Нажмите здесь для печати.