Методы обмена данными между PHP-процессами: от разделяемой памяти до файлового кэша
Основные способы обмена данными в PHP
Как организовать быстрый обмен переменными между разными скриптами PHP на одном сервере?
Наиболее эффективное решение – использование разделяемой памяти System V через функции shm_attach, shm_put_var, shm_get_var. Данные хранятся в оперативной памяти и доступны всем процессам, знающим общий ключ.
<?php
// Скрипт 1: запись
$key = ftok(__FILE__, 't');
$shm = shm_attach($key, 1024, 0777); // размер 1KB
$data = ['user' => 'admin', 'time' => time()];
shm_put_var($shm, 1, $data);
echo "Данные записаны";
shm_detach($shm);
?>
Share php (функция share в php)
<?php
// Скрипт 2: чтение
$key = ftok(__FILE__, 't'); // тот же файл для ftok
$shm = shm_attach($key, 1024, 0777);
if (shm_has_var($shm, 1)) {
$data = shm_get_var($shm, 1);
print_r($data);
} else {
echo "Данных нет";
}
shm_detach($shm);
?>
Типичные ошибки:
- Несовпадение ключа – используйте
ftokс одинаковым файлом. - Переполнение выделенной памяти – увеличьте второй аргумент
shm_attach. - Права доступа – укажите 0777, чтобы другие процессы могли читать/писать.
- Отсутствие расширения
sysvshm– проверьте phpinfo().
Цель: высокая скорость, данные в RAM, подходит для флагов, счетчиков, конфигураций.
Как реализовать обмен данными без установки дополнительных расширений?
Используйте файловую систему с блокировками. Данные сериализуются и сохраняются в общий файл.
<?php
$file = '/tmp/shared.dat';
$data = ['views' => 10];
$fp = fopen($file, 'c+');
if (flock($fp, LOCK_EX)) {
ftruncate($fp, 0);
fwrite($fp, serialize($data));
flock($fp, LOCK_UN);
}
fclose($fp);
?>
Проблемы:
- Медленнее памяти, особенно при высоких нагрузках.
- Риск повреждения данных при аварийном завершении.
- Необходимость синхронизации через
flock.
Цель: простой способ для небольших проектов без прав на установку расширений.
Как использовать APCu для обмена данными между запросами?
APCu (APC User Cache) хранит данные в разделяемой памяти, но доступен только в рамках одного сервера.
<?php
// Запись
apcu_store('my_var', ['value' => 123], 3600);
// Чтение
$val = apcu_fetch('my_var');
if (apcu_exists('my_var')) {
echo $val['value'];
}
?>
Ошибки:
- APCu не установлен – требуется расширение apcu.
- Данные хранятся только в памяти одного сервера, не для кластеров.
Цель: кэширование и быстрый обмен между скриптами одного хоста.
Как синхронизировать доступ к ресурсу между процессами?
Семафоры System V (sem_get, sem_acquire) часто применяются вместе с разделяемой памятью.
<?php
$sem_id = sem_get(1234, 1, 0777);
sem_acquire($sem_id);
// критическая секция – работа с разделяемой памятью
$shm = shm_attach(ftok(__FILE__, 't'));
$counter = shm_get_var($shm, 1);
$counter++;
shm_put_var($shm, 1, $counter);
shm_detach($shm);
sem_release($sem_id);
?>
Ошибки:
- Забыли освободить семафор – возникает deadlock.
- Семафор не удалён после завершения – накапливаются.
Цель: атомарные операции с общими данными.
Расширенные примеры с пояснениями
Пример 1: Разделяемая память с несколькими переменными
<?php
$key = ftok('/tmp/test.php', 'R');
$shm = shm_attach($key, 4096);
// Запись разных типов данных
shm_put_var($shm, 1, 'строка');
shm_put_var($shm, 2, 12345);
shm_put_var($shm, 3, ['a' => 1, 'b' => 2]);
echo "Данные сохранены.";
shm_detach($shm);
?>
Данные сохранены.
Пример 2: Чтение и проверка существования переменной
<?php
$key = ftok('/tmp/test.php', 'R');
$shm = shm_attach($key, 4096);
if (shm_has_var($shm, 1)) {
$str = shm_get_var($shm, 1);
echo "Переменная 1: $str\n";
} else {
echo "Переменной 1 нет.\n";
}
if (shm_has_var($shm, 3)) {
$arr = shm_get_var($shm, 3);
print_r($arr);
}
shm_detach($shm);
?>
Переменная 1: строка
Array
(
[a] => 1
[b] => 2
)
Пример 3: Сброс и удаление разделяемой памяти
<?php
$key = ftok('/tmp/test.php', 'R');
$shm = shm_attach($key, 4096);
// Удаление одной переменной
shm_remove_var($shm, 2);
echo "Переменная 2 удалена.\n";
// Полный сброс сегмента
shm_remove($shm);
echo "Сегмент разделяемой памяти удалён.\n";
?>
Переменная 2 удалена. Сегмент разделяемой памяти удалён.
Пример 4: Файловый обмен с использованием JSON
<?php
$file = '/tmp/data.json';
// Запись
$data = ['login' => 'user1', 'ip' => '192.168.0.1'];
$wrapper = fopen($file, 'c+');
flock($wrapper, LOCK_EX);
ftruncate($wrapper, 0);
fwrite($wrapper, json_encode($data));
fflush($wrapper);
flock($wrapper, LOCK_UN);
fclose($wrapper);
echo "Запись завершена.\n";
// Чтение
$content = file_get_contents($file);
$parsed = json_decode($content, true);
print_r($parsed);
?>
Запись завершена.
Array
(
[login] => user1
[ip] => 192.168.0.1
)
Пример 5: Использование APCu для подсчёта просмотров
<?php
$key = 'page_views';
if (!apcu_exists($key)) {
apcu_store($key, 0);
}
apcu_inc($key);
echo "Просмотров: " . apcu_fetch($key);
?>
Просмотров: 3
Нестандартный пример: Обмен через сокеты Unix Domain
<?php
// Сервер (скрипт 1)
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
$path = '/tmp/shared_socket';
@unlink($path);
socket_bind($socket, $path);
socket_listen($socket);
$client = socket_accept($socket);
$msg = "Hello from server";
socket_write($client, $msg, strlen($msg));
socket_close($client);
socket_close($socket);
?>
<?php
// Клиент (скрипт 2)
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
socket_connect($socket, '/tmp/shared_socket');
$response = socket_read($socket, 1024);
echo $response;
socket_close($socket);
?>
Hello from server