Проверка рабоспособности flock на практике.
Для чего это написано.
Представьте, у вас есть нагруженная система (много посетителей), которая хранит какую-то информацию в файлах. Очевидно, любые скрипты по записи и чтении файлов могут работать параллельно и мешать друг-другу. Еще очевидно, что в таких операциях нужно использовать блокировку файлов, но даже в этом может быть проблема. Маленькая и незаметная, она проявляется только при большой нагрузке на сервер.
Сделаем 2 скрипта. Первый - будет писать в файл случайное число (берется из rand) на первой строке файла и md5 проверку этого числа строкой ниже. Прежде, чем записать файл скрипт его обрезает для записи - режим w. Второй - читает обе строки из файла и следит, что md5 соответствует числу из первой строки, т.к. как бы проверяет целостность файла.
В начале простой пример. Перезапись одного файла работает без проблем:
> /tmp/11qq")); // заблокировали flock($f,2);
// считали число $r=intval(fgets($f,100)); // проверка глюков - число не должно быть меньше 1 000 000 ! if ($r> /tmp/11qq"); // указатель на начало + запись обратно увеличенного числа fseek($f,0); fputs($f,($r+1)." "); fclose($f); ?> |
А вот чтение без блокировки с параллельной записью нарушает целостность данных. Делаем 2 скрипта - первый будет постоянно писать в файл, второй постоянно читать. Запись идет с блокировкой, чтение - без. Для теста запустите 2 утилиты, чтобы параллельно слать запросы на оба скрипта.
запись:
> /tmp/11qq")); flock($f,2);
function make_seed() { list($usec,$sec) = explode(" ", microtime()); return ((float)$sec+(float)$usec) * 100000; } mt_srand(make_seed()); $s = mt_rand(); $s=$s." ".microtime(); $md=md5($s); fputs($f,"$s\n$md\n"); fclose($f); ?> |
чтение
> /tmp/11qq")); $s=trim(fgets($f,100)); $md=trim(fgets($f,100)); if ($md!=md5($s)) system("echo error_md5 >> /tmp/11qq"); fclose($f); ?> |
Каково же было мое удивление, когда от добавления flock в скрипт чтения ничего не изменилось...
> /tmp/11qq")); flock($f,2); $s=trim(fgets($f,100)); $md=trim(fgets($f,100)); if ($md!=md5($s)) { system("echo error_md5 >> /tmp/11qq"); echo "md5 error"; $ff=fopen ("/tmp/11log.txt","a+"); flock($ff,2); fputs($ff,time()." ".filesize("flock2_test.txt")." [$s|$md]\n"); fclose($ff); } fclose($f); ?> |
Потом я проделал 2 хитрых фичи:
заменил
$f=fopen("flock2_test.txt","r+") or die(system("echo fopen_write >> /tmp/11qq")); flock($f,2); |
flock($f=fopen("flock2_test.txt","r+"),2); |
flock($f,1); flock($f,2); |
Третья попытка удалась и проблема с активным чтением и записью решилась. Просто надо было в скрипте записи открывать файл не на обнуление (w), а только чтение+запись (r+). Одним словом проблема решилась заменой скрипта записи:
// открыли файл на чтение+запись $f=fopen("flock2_test.txt","r+") or die(system("echo fopen_write >> /tmp/11qq")); // заблокировали flock($f,2); // обнулили до 0 байт (как бы стерли) ftruncate($f,0); |