У меня есть файл журнала, поддерживаемый скриптом PHP. Сценарий PHP подлежит параллельной обработке. Я не могу заставить механизм flock()
работать с файлом журнала: в моем случае flock()
не предотвращает одновременный доступ к файлу журнала, совместно используемому сценариями PHP, работающими параллельно, и иногда перезапись.
Я хочу иметь возможность читать файл, выполнять некоторую обработку, изменять данные и записывать обратно без того, чтобы один и тот же код выполнялся параллельно на сервере, делая то же самое одновременно. Чтение-модификация-запись должны выполняться последовательно.
На одном из моих общих хостингов (OVH France) он не работает должным образом. В этом случае мы видим, что счетчик $c
имеет одинаковое значение в разных iframe
, что невозможно, если блокировка работает должным образом, как это происходит на другом виртуальном хостинге.
Любые предложения, чтобы сделать эту работу, или для альтернативного метода?
Поиск в Google "read modify write" php
или fetch and add
или test and set
не дал полезной информации: все решения основаны на работающем flock().
Вот некоторый автономный демонстрационный код для иллюстрации. Он генерирует ряд параллельных запросов от браузера к серверу и отображает результаты. Визуально легко заметить дисфункцию: если ваш веб-сервер не поддерживает flock(), как у меня, то значение счетчика и количество строк лога будут одинаковыми в некоторых кадрах.
<!DOCTYPE html>
<html lang="en">
<title>File lock test</title>
<style>
iframe {
width: 10em;
height: 300px;
}
</style>
<?php
$timeStart = microtime(true);
if ($_GET) { // iframe
// GET
$time = $_GET['time'] ?? 'no time';
$instance = $_GET['instance'] ?? 'no instance';
// open file
// $mode = 'w+'; // no read
// $mode = 'r+'; // does not create file, we have to lock file creation also
$mode = 'c+'; // read, write, create
$fhandle = fopen(__FILE__ .'.rwtestfile.txt', $mode) or exit('fopen');
// lock
flock($fhandle, LOCK_EX) or exit('flock');
// start of file (optional, only some modes like require it)
rewind($fhandle);
// read file (or default initial value if new file)
$fcontent = fread($fhandle, 10000) or ' 0';
// counter value from previous write is last integer value of file
$c = strrchr($fcontent, ' ') + 1;
// new line for file
$fcontent .= "<br />\n$time $instance $c";
// reset once in a while
if ($c > 20) {
$fcontent = ' 0'; // avoid long content
}
// simulate other activity
usleep(rand(1000, 2000));
// start of file
rewind($fhandle);
// write
fwrite($fhandle, $fcontent) or exit('fwrite');
// truncate (in unexpected case file is shorter now)
ftruncate($fhandle, ftell($fhandle)) or exit('ftruncate');
// close
fclose($fhandle) or exit('fclose');
// echo
echo "instance:$instance c:$c<br />";
echo $timeStart ."<br />";
echo microtime(true) - $timeStart ."<br />";
echo $fcontent ."<br />";
} else {
echo 'File lock test<br />';
// iframes that will be requested in parallel, to check flock
for ($i = 0; $i < 14; $i++) {
echo '<iframe src="?instance='. $i .'&time='. date('H:i:s') .'"></iframe>'."\n";
}
}
В PHP: flock - Manual есть предупреждение об ограничениях flock()
, но речь идет об ISAPI (Windows) и FAT (Windows). Конфигурация моего сервера:
Версия PHP 7.2.5
Система: Linux cluster026.gra.hosting.ovh.net
API сервера: CGI/FastCGI