PHP Java, або In-memory кластер тепер і для PHP розробників, SavePearlHarbor
Ще одна копія хабора
Головне меню
Навігація за записами
PHP + Java, або In-memory кластер тепер і для PHP розробників

Більшість 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.
Навіщо потрібний IMDG?
Найвужчим місцем багатьох високонавантажених проектів є сховище даних, зокрема реляційна база даних. Для боротьби з вадами традиційних БД в основному використовується 2 підходи:
1) Кешуванняплюси:
- висока швидкість доступу до даних
- дуже рідко зустрічаються справжні кластерні рішення, переважно користувачеві самому доводиться займатися розподілом даних по серверах, а при доступі до данихвизначати сервер, у якому лежать ці дані. Рівномірності заповненості всіх вузлів кластера у такій системі досягти складно
- вимагає компромісу між актуальністю даних та швидкістю доступу, т.к. дані в кеші можуть застаріти, а видаляти старі дані з кешу з наступним кешуванням нових - це додаткові затримки та навантаження на систему
- Зазвичай дані кешуються над вигляді доменних об'єктів, які у додатку, а вигляді BLOB чи рядків, тобто. при використанні даних, отриманих з кешу, потрібно спочатку сформулювати необхідні об'єкти
2) NoSQL рішенняплюси:
- гарна горизонтальна масштабованість
- не така висока швидкість отримання результатів у разі використання диска
- практично неможливо забезпечити роботу внутрішньокорпоративного софту, орієнтованого на роботу з конкретною реляційною БД
IMDG поєднує переваги обох підходів і при цьому має низку переваг перед згаданими вище рішеннями:
- гарна горизонтальна масштабованість
- висока швидкість доступу
- справжня кластеризація (класти дані можна на будь-який вузол, запитувати дані можна також на будь-якому вузлі кластера), автоматичне балансування даних між вузлами
- кластер знає про всі поля об'єкта, отже можна шукати об'єкти не тільки за ключами, але й значеннями полів
- є можливість створювати індекси по полях або їх комбінації
- при використанні механізмів read-through і write-behind (або write-through) дані синхронізуватимуться з БД, що дозволить іншим додаткам (або іншим модулям додатку) продовжувати користуватися традиційною БД (MySQL або Mongo - неважливо)
- З використанням схеми роботи з попереднього пункту зникає проблема актуалізації даних у кеші, т.к. вони завжди там будуть такі самі, як і в БД
Розглянемо ближче ці 2 цікаві механізми: read-through і write-behind (write-through)
read-through
write-behind (write-through)
Для оптимізації швидкості запису ви можете писати не БД, а безпосередньо в кеш. Звучить на перший погляд дивно, але на практиці це добре розвантажує БД та підвищує швидкість роботи програми. Виглядає це приблизно так:
- Користувач робить викликcache.put(key, value), об'єкт 'value' зберігається в кеші за ключом 'key'
- У кластері спрацьовує обробник цієї події, відбувається складання sql-запиту для запису даних у БД та його виконання
- Управління повертається користувачеві
Така схема взаємодії називаєтьсяwrite-through. Вона дозволяє синхронізувати оновлення з БД одночасно з оновленнями у кластері. Як можна помітити, такий підхід не прискорює процес запису даних, але забезпечує узгодженість між кешем і БД. Також при такому вигляді запису дані потрапляють у кеш, а отже доступ до них на читання все одно буде вищим, ніж запит до БД.
Якщо ж одночасна запис БД не є критичним умовою, тоді можна використовувати більш популярний механізм write-behind , він дозволяє організувати відкладений запис БД (будь-який інший сторадж). Приблизно так:
- Користувач робить викликcache.put(key, value), об'єкт 'value' зберігається в кеші за ключом 'key'
- Управління повертається користувачеві
- Через деякий час (конфігурується користувачем) спрацьовує обробник подіїзапису в кеш
- Оброблювач збирає всю пачку об'єктів, які були змінені з часу попереднього спрацювання оброблювача
- Пачка вирушає до БД на запис
При використанні write-behind операція запису істотно прискорюється, тому що користувач не чекає, поки апдейт дійде до БД, а просто кладе дані в кеш, а всі апдейти одного і того ж об'єкта будуть злиті в один результуючий апдейт, при цьому запис в БД відбувається пачками, що теж позитивно б'є по завантаженні сервера БД, Таким чином можна налаштувати свій IMDG те щоб кожні 3 секунди (чи 2 хв, чи 50 мс) все оновлення даних асинхронно відправлялися до бази.
Що з цього є у Sproot Grid?
У першій версії вирішив не реалізовувати відразу все, що розповів вище, т.к. це забрало б багато часу, а мені хотілося б якнайшвидше отримати фідбек від користувачів. Отже, що доступно в Sproot Grid 1.0.0:
- Горизонтальна масштабованість та чесна кластеризація з балансуванням кількості даних між вузлами кластера
- Можливість зберігання як вбудованих типів PHP, так і доменних об'єктів
- Можливість побудови індексу по полю та пошуку за цим індексом
Getting Started
Спочатку вам треба завантажити дистрибутив звідси та розпакувати його.
Встановлення необхідного ПЗ
Так як JBoss Infinispan - це Java додаток, то необхідно було вибрати спосіб взаємодії між Java та PHP. Як така сполучна ланка був обраний Apache Thrift (протокол був розроблений для серіалізації та транспорту між вузлами в Cassandra), тому для того, щоб Sproot Grid міг працювати на вашій системі необхідно встановити таке:
- Java
- Thrift - установка вproduction не потрібно, установка потрібна лише на девелоперській машині (подробиці у пункті Генерація коду). При депло в production вам потрібно тільки скопіювати .php файли бібліотеки Thrift і java бібліотеку у форматі .jar
- PHP (якщо ще не встановлено)
Інструкції зі встановлення розташовані на wiki проекту
Конфігурація
Файл конфігурації повинен знаходитися в $deploymentFolder/sproot-grid/config/definition.xml, де deploymentFolder — це шлях до директорії, в якій ви розпакували дистрибутив
Як можна помітити з конфігурації, для кожного типу об'єктів ми можемо прописати ім'я кеша (а можемо не прописувати, якщо не хочемо зберігати такі об'єкти в окремому кеші). Cache - це хеш-таблиця, розподілена за кластером, в кластері може бути скільки завгодно кешів. В одному кеші можуть зберігатися лише об'єкти того самого типу. Всі кеші повинні бути описані в секції. У конфігурації є окрема секція для опису структури кластера і список кешів, які зберігатимуться в ньому.
— опис типів, які зберігатимуться у вашому кластері. Можна використовувати як вбудовані типи PHP, так і кастомні. Як можна помітити, для кожного типу об'єктів ми можемо прописати ім'я кеша (а можемо і не прописувати, якщо не хочемо зберігати такі об'єкти в окремому кеші)
Генерація коду для інтеграції з вашим додатком
Для ефективної роботи кластеру необхідно згенерувати код, специфічний для вашої програми (вашої доменної моделі) і скомпілювати його Java частину, оскільки це працює швидше, ніж доступ до об'єктів через reflection. Щоб згенерувати та скомпілювати весь необхідний код, треба:
, де $deploymentFolder це той каталог, в який ви розпакували дистрибутив Генерацію кодунеобхідно робити лише у разі зміни опису доменної моделі, тобто. якщо ваша модель стабільна, то цю операцію вам доведеться зробити лише один раз, після цього згенеровані php вихідники можна зберігати в репозиторії коду, а частина java буде скомпільована в бібліотеку. Тобто. не треба нічого генерувати по 10 разів перед тим, як задеплоїти вашу програму, це робиться тільки 1 раз на етапі розробки. Після закінчення виконання генерації коду, скопіюйте папку з .php файлами з $deploymentFolder/sproot-grid/php/org у корінь вашої програми
, де nodeId — значення атрибута id секції в конфігураційному файлі, memorySize — кількість пам'яті (у Мб або Гб), які ви хочете виділити вузлу
Використання всередині програми
На кроці генерації коду ви отримали все необхідне інтеграції з вашим додатком. Залишилось тільки скопіювати цей код у свою програму, для цього скопіюйте все з папки $deploymentFolder/sproot-grid/php в корінь своєї програми Все! Тепер можете використовувати кластер зі своєї програми.
Опис API можете знайти тут, але якщо коротко, то API зараз такий:
- get($cacheName, $key)
- getAll($cacheName, array $keys)
- cacheSize($cacheName)
- cacheKeySet($cacheName)
- containsKey($cacheName, $key)
- search($cacheName, $fieldName, $searchWord)
- remove($cacheName, $key)
- removeAll($cacheName, array $keys)
- put($cacheName, $key, $domainObject)
- putAll($cacheName, array $domainObjects)
- clearCache($cacheName)