Запись наборов данных в общую память с помощью PHP

Пошаговое руководство по использованию общей памяти PHP в качестве накопителя

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

Клаус Силвейра, инженер-программист и инструктор, 4Linux

Клаус СилвейраКлаус Силвейра (Klaus Silveira) ― инженер-программист и инструктор, специализирующийся на PHP, увлечен разработкой интеллектуальных решений для Интернета и участвует в таких проектах по разработке ПО с открытым исходным, как SimpleString и SimpleSHM. Работает в бразильской компании 4Linux, специализирующейся на бесплатных программных решениях. Отвечает за разработку корпоративных Web-приложений и курсов по PHP, на которых готовят разработчиков к сдаче сертификационных экзаменов по Zend PHP.



04.12.2012

Обзор

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

Это означает, что приложение, написанное на языке C, может обмениваться информацией с приложением, написанным на другом языке, таком как Java™ или PHP. Они могут обмениваться информацией, если только способны получить и понять эту информацию. Общая память широко применяется в реализациях, доступных для большинства языков, поэтому получение доступа не должно быть проблемой. Что касается понимания информации, то можно использовать стандартный формат, такой как XML или JSON.

Общая память представляет собой быстрый способ обмена данными между процессами, главным образом потому, что после создания сегментов ядро операционной системы не принимает никакого участия в процессе передачи данных. Подобные методы часто называют "межпроцессным взаимодействием" (interprocess communication - IPC). В число других методов IPC входят конвейеры, очереди сообщений, RPC и сокеты. Такой быстрый и надежный способ обмена данными между приложениями неоценим при работе с экосистемой приложений, которым нужно общаться друг с другом. Обычный метод использования баз данных для обмена информацией между приложениями часто приводит к медленной обработке запросов и даже блокированию ввода-вывода в зависимости от размера экосистемы. При работе с общей памятью операции ввода-вывода, замедляющие процесс, отсутствуют.

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

Общая память и PHP

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


Создание сегментов

Функции общей памяти аналогичны функциям управления файлами, только вместо потоков вы работаете с идентификаторами доступа к общей памяти. Первым примером служит функция shmop_open, которая позволяет открыть существующий или создать новый сегмент. Эта функция очень похожа на классическую функцию fopen, которая открывает потоки для манипуляции файлами, возвращая ресурс, доступный для использования другими функциями, выполняющими операции чтения или записи в этот открытый поток. Рассмотрим функцию shmop_open, приведенную в листинге 1.

Листинг 1. Функция shmop_open
<?php 

$systemid = 864; // Системный идентификатор сегмента общей памяти
$mode = "c"; // Режим доступа 
$permissions = 0755; // Разрешения для сегмента общей памяти 
$size = 1024; // Размер сегмента в байтах 

$shmid = shmop_open($systemid, $mode, $permissions, $size); 

?>

Первый параметр ― system ID. Это число, определяющее сегмент общей памяти в системе. Второй параметр ― режим доступа, который очень похож на режим доступа функции fopen. Доступ к сегменту можно получить четырьмя способами:

  • режим «а» позволяет обращаться к сегменту только для чтения;
  • режим «w» позволяет обращаться к сегменту для чтения и записи;
  • в режиме «c» создается новый сегмент, а если он уже существует, предпринимается попытка открыть его для чтения и записи;
  • режим «n» создает новый сегмент, а если он уже существует, выдается сообщение об ошибке.

Третий параметр ― разрешения для сегмента. Здесь нужно указать восьмеричное число.

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

Обратите внимание, что эта функция возвращает идентификационный номер, который можно использовать с другими функциями для манипулирования сегментом общей памяти. Это идентификатор доступа к общей памяти, отличный от системного идентификатора, передаваемого в качестве параметра. Не путайте их. В случае неудачи функция shmop_open возвратит значение FALSE.


Запись в сегменты

Для записи данных в блок общей памяти используйте функцию shmop_write. Она очень проста в применении и принимает всего три параметра. См. листинг 2.

Листинг 2. Применение функции shmop_write для записи в блок общей памяти
<?php 

$shmid = shmop_open(864, 'c', 0755, 1024); 
shmop_write($shmid, "Hello World!", 0); 

?>

Она похожа на функцию fwrite, которая принимает два параметра: открытый ресурс потока, возвращаемый функцией fopen, и данные, подлежащие записи. Функция shmop_write делает то же самое.

Первый параметр — идентификатор, возвращаемый функцией shmop_open, ― определяет блок общей памяти, с которым вы собираетесь работать. Второй параметр — это данные, которые нужно хранить и, наконец, третий параметр определяет адрес начала записи. По умолчанию это 0, так что запись начинается с самого начала. В случае неудачи эта функция возвращает значение FALSE и количество успешно записанных байтов.


Чтение из сегментов

Чтение из сегментов общей памяти ― простая процедура. Достаточно открыть сегмент и воспользоваться функцией shmop_read. Она принимает несколько параметров и работает аналогично функции fread. В листинге 3 приведен пример чтения содержимого файла в PHP.

Листинг 3. Использование функции shmop_read для чтения содержимого файла
<?php 

$stream = fopen('file.txt', 'r+'); 
fwrite($stream, "Hello World!"); 
echo fread($stream, 11); 

?>

Чтение содержимого сегмента общей памяти выполняется аналогично, как показано в листинге 4.

Листинг 4. Чтение содержимого сегмента общей памяти
<?php

$shmid = shmop_open(864, 'c', 0755, 1024);
shmop_write($shmid, "Hello World!", 0);
echo shmop_read($shmid, 0, 11);

?>

Обратите внимание на параметры. Функция shmop_read принимает идентификатор, возвращенный функцией shmop_open, который нам уже знаком, и два других параметра. Второй параметр ― место, с которого нужно начать чтение сегмента; а третий ― количество байтов, которое требуется считать. Второй параметр может всегда быть 0, началом данных, зато третий может вызвать проблему, так как мы можем не знать, сколько байтов хотим считать.

Это очень похоже на поведение функции fread, которая принимает два параметра: открытый ресурс потока, возвращаемый функцией fopen, и количество байтов, которые требуется считать из этого потока. Чтобы прочитать файл во всей его полноте, используйте функцию filesize, которая возвращает количество байтов в файле.

К счастью, при работе с сегментами общей памяти функция shmop_size, подобно функции filesize, возвращает размер сегмента в байтах. (См. листинг 5).

Листинг 5. Функция shmop_size возвращает размер сегмента в байтах
<?php 

$shmid = shmop_open(864, 'c', 0755, 1024); 
shmop_write($shmid, "Hello World!", 0); 

$size = shmop_size($shmid); 
echo shmop_read($shmid, 0, $size); 

?>

Удаление сегмента

Мы знаем, как открывать, записывать и считывать сегменты общей памяти. Для завершения нашего класса по CRUD осталось только узнать, как удалять сегменты. Эту задачу можно легко решить с помощью функции shmop_delete, которой нужен только один параметр: идентификатор сегмента общей памяти, который мы хотим удалить.

Листинг 6. Функция shmop_delete помечает сегмент для удаления
<?php 

$shmid = shmop_open(864, 'c', 0755, 1024); 
shmop_write($shmid, "Hello World!", 0); 
shmop_delete($shmid); 

?>

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


Закрытие сегмента

При открытии сегмента общей памяти вы «присоединяетесь» к нему. Присоединившись к сегменту, в него можно записывать данные и считывать их из него, но когда работа закончена, нужно отсоединиться от сегмента. Это делается с помощью функции shmop_close, как показано в листинге 7.

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

Листинг 7. Использование функции shmop_close для отсоединения от сегмента
<?php 

$shmid = shmop_open(864, 'c', 0755, 1024); 
shmop_write($shmid, "Hello World!", 0); 
shmop_delete($shmid); 
shmop_close($shmid); 

?>

Использование общей памяти в качестве накопителя

Обладая базовыми знаниями в области общей памяти и освоив основные операции CRUD над сегментами общей памяти, можно приступать к практическому применению этих знаний. Общую память можно использовать в качестве уникальной альтернативы накопителю, которая предлагает такие преимущества, как быстрые операции чтения/записи и взаимодействие процессов. Для Web-приложения это означает:

  • буферную память (хранение запросов к базе данных, данных Web-сервисов, внешних данных);
  • память сеансов;
  • обмен данными между приложениями.

Прежде чем продолжить, я хотел бы представить небольшую библиотеку SimpleSHM. Это компактный уровень абстракции для работы с общей памятью в PHP, который позволяет легко манипулировать сегментами с применением объектно-ориентированного подхода. Эта библиотека помогает создавать гораздо более чистый код при написании небольших приложений, использующих общую память для хранения данных. Для начала работы с SimpleSHM загрузите tarball-файл со страницы GitHub.

Существует три вида операций: чтение, запись и удаление. Простое создание экземпляра объекта класса обеспечит открытие сегмента общей памяти. Основные моменты отражены в листинге 8.

Листинг 8. Основы использования SimpleSHM
<?php

$memory = new SimpleSHM;
$memory->write('Sample');
echo $memory->read();

?>

Обратите внимание, что идентификатор классу не передается. Раз идентификатор не передается, номер будет выбран случайным образом, и новый сегмент откроется с этим номером. Номер можно передать конструктору в качестве параметра, чтобы сегмент открывался или создавался с конкретным значением ID, как показано в листинге 9.

Листинг 9. Открытие заданного сегмента
<?php

$new = new SimpleSHM(897);
$new->write('Sample');
echo $new->read();

?>

Магический метод __destructor заботится о вызове функции shmop_close по отношению к сегменту, чтобы отключить объект и отсоединить его от сегмента. Назовем это "SimpleSHM 101". Теперь воспользуемся им для более высокой цели: использования общей памяти в качестве накопителя. Для хранения наборов данных требуется сериализация, поскольку в памяти нельзя хранить массивы или объекты. Здесь для сериализации используется JSON, но подойдет и любой другой метод, например, XML или встроенные функции сериализации PHP. Пример приведен в листинге 10.

Листинг 10. Использование общей памяти в качестве накопителя
<?php

require('SimpleSHM.class.php');

$results = array(
	'user' => 'John',
	'password' => '123456',
	'posts' => array('My name is John', 'My name is not John')
);

$data = json_encode($results);

$memory = new SimpleSHM;
$memory->write($data);
$storedarray = json_decode($memory->read());

print_r($storedarray);

?>

Мы успешно преобразовали массив в строку JSON, сохранили его в блоке общей памяти, считали, преобразовали строку JSON обратно в массив и отобразили его. Все это кажется тривиальным, но только представите себе, какие возможности открывает этот фрагмент. Его можно использовать для хранения результатов запросов к Web-сервису, базам данных или даже в качестве буфера процессора текстовых шаблонов. Запись и чтение из памяти обеспечит гораздо более высокую производительность, чем запись и чтение с диска.

Этот метод хранения полезен не только для использования в качестве буфера, но и для обмена данными между приложениями, если эти данные хранятся в формате, понятном обоим приложениям. Нельзя недооценивать возможности общей памяти в Web-приложениях. Существует множество различных способов эффективной реализации такого хранения, и единственное ограничение здесь ― талант и мастерство программиста.


Заключение

Эта статья охватывает большую часть инструментов PHP для работы с сегментами общей памяти и объясняет, как функционирует общая память. Более того, она содержит предложения по совершенствованию Web-приложений и обращает внимание на некоторые факторы, которые нужно иметь в виду при создании Web-приложений. Идеи и рекомендации по их осуществлению станут отправной точкой для читателя. А построенная нами базовая модель поможет ему обдумать более сложные функции и решения.

Что дальше?

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

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=848511
ArticleTitle=Запись наборов данных в общую память с помощью PHP
publish-date=12042012