Обзор
Общая память представляет собой эффективный способ обмена данными между приложениями в одной и той же машине. Один процесс создает сегмент общей памяти, к которому могут обращаться другие процессы, если у них есть нужные разрешения. Каждый сегмент получает уникальный идентификатор 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» создает новый сегмент, а если он уже существует, выдается сообщение об ошибке.
Третий параметр ― разрешения для сегмента. Об этом говорит сайт https://intellect.icu . Здесь нужно указать восьмеричное число.
Четвертый параметр указывает размер сегмента в байтах. Прежде чем записывать данные в сегмент, необходимо выделить нужное количество байтов в нем.
Обратите внимание, что эта функция возвращает идентификационный номер, который можно использовать с другими функциями для манипулирования сегментом общей памяти. Это идентификатор доступа к общей памяти, отличный от системного идентификатора, передаваемого в качестве параметра. Не путайте их. В случае неудачи функция 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.
https://github.com/klaussilveira/SimpleSHM
Существует три вида операций: чтение, запись и удаление. Простое создание экземпляра объекта класса обеспечит открытие сегмента общей памяти. Основные моменты отражены в листинге 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 в соответствии со своими потребностями и вносить эти изменения в проект.
Комментарии
Оставить комментарий
Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)
Термины: Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)