Php блокировка

 упила угловую м€гкую мебель этой фабрики - отличное качество по ценам производител€!

Php-web-дизайн > Php блокировка

—уть проблемы такова:
≈сть база данных, используема€ на сайте (например, база дл€ регистрации пользователей, куда записываетс€ их им€ и email), она лежит в текстовом файле построчно (в дальнейшем, "file_base.dat".). ƒва пользовател€ активизируют сервер через командную строку в броузере, дл€ ввода свох имен и email. —ервер отсылает их к скрипту. ќба пользовател€ "начинают движение" по скриптовому потоку (тексту php файла) сверху вниз, причем, ѕервый "бежит" на долю секунды быстрее ¬торого.  огда они достигают того места, где скрипт исполн€ет их запрос, движение по потоку останавливаетс€, в их броузер выводитс€ сгенерированна€ скриптом страница в виде html. „тобы из file_base.dat прочитать данные, этот файл надо открыть на чтение (функци€ - @file), чтобы записать что-то в него, надо открыть на запись (функци€ - @fopen). ¬ скрипте это выгл€дит так:

<?
ЕЕ
// читаем данные из файла-базы
$f = @file ("file_base.dat", "r"); // здесь находитс€ 2 пользователь
// здесь идет текст скрипта
// открываем файл-базу на запись
$fp = @fopen ("file_base.dat", "w"); // здесь находитс€ 1 пользователь
// записываем в файл-базу данные из выше
// прочитанного file_base.dat - переменна€ $f
// и добавл€ем еще одну строку с данными нового пользовател€
// закрываем файл-базу
@fclose ($fp);
ЕЕ
?>

“о есть, чтобы новый пользователь по€вилс€ в нашей file_base.dat, мы считываем оттуда информацию, которую кладем в переменную $f, затем записываем в этот же файл эту переменную $f (при этом, file_base.dat переписываем полностью) и внизу дописываем еще одну строку с данными нашего нового пользовател€. ћы видим, что на чтение и на запись file_base.dat открываетс€ в разных местах нашего скрипта. ≈сли оба пользовател€, одновременно, достигли "своего" места в этом скрипте, но один из file_base.dat только начал читать данные, а другой уже их прочитал, продвинулс€ чуть ниже и, в этот момент, находитс€ на отметке записи в файл file_base.dat своих данных. “о, в этом случае, от нашего файла file_base.dat ничего не останетс€. ¬ св€зи с этим, в Php была введена функци€ совместного доступа @flock, котора€ призвана не допускать совместный доступ к файлу на чтение и запись.

<?
ЕЕ
// читаем данные из файла файла-базы
$f = @file ("file_base.dat", "r"); // здесь находитс€ 2 пользователь
// здесь идет текст скрипта
// открываем файл-базу на запись
$fp = @fopen ("file_base.dat", "w"); // здесь находитс€ 1 пользователь
// блокируем файл-базу на чтение
@flock ($fp, LOCK_EX)
// записываем в файл-базу данные из выше
// прочитанного file_base.dat - переменна€ $f
// и добавл€ем еще одну строку с данными нового пользовател€
// снимаем блокировку
@flock ($fp, LOCK_UN)
// закрываем файл-базу
@fclose ($fp);
ЕЕ
?>

“еперь, вроде бы все нормально, файл блокируетс€ на чтение, когда в него что-то пишут. Ќо, что произойдет в нашем случае, когда, 1 пользователь прочитал данные файла и пришел к отметке открыти€ file_base.dat на запись, оркыл его и заблокировал на чтение функцией @flock, а 2 пользователь, именно в этот момент пришел к точке, где данные из файла считываютс€ функцией @file? 2 пользователь ничего не прочитает из этого файла, потому что file_base.dat блокирован 1 пользователем командой @flock на чтение. 2 пользователь просто "пробежит" дальше по потоку скрипта ничего из файла file_base.dat не прочитав, то есть наша переменна€ $f будет пустой. Ќо, дальше-то он будет записывать в этот файл, то что ранее с него считал. ¬ итоге, в наш file_base.dat запишетс€ только одна строка с его именем и email. “о есть, file_base.dat будет потер€н.
¬ св€зи с чем, была придумана функци€: read_file

function read_file($path)
{
if(!is_file($path)) {return false; }
elseif(!filesize($path)) {return array(); }
elseif($array=file($path)) {return $array; }
else { while(!$array=file($path)){sleep(1);} return $array; }
}

—уть ее такова: пока файл блокирован на чтение, пользователь, который хочет считать из него информацию, находитс€ в цикле, то есть, как бы "стоит" на месте, не "бежит" дальше по тексту скрипта, ожида€, когда файл разблокируетс€ на чтение. ¬ итоге наш скрипт принимает такой вид:

<?
function read_file($path)
{
if(!is_file($path)) {return false; }
elseif(!filesize($path)) {return array(); }
elseif($array=file($path)) {return $array; }
else { while(!$array=file($path)){sleep(1);} return $array; }
}
ЕЕ
// провер€ем заблокирован ли файл на чтение,
// если заблокирован, назначаем цикл с остановкой,
// пока блокировка не будет сн€та, после сн€ти€ блокировки,
// читаем данные из файла файла-базы
$f = read_file ("file_base.dat", "r"); // здесь находитс€ 2 пользователь
// здесь идет текст скрипта
// открываем файл-базу на запись
$fp = @fopen ("file_base.dat", "w"); // здесь находитс€ 1 пользователь
// блокируем файл-базу на чтение
@flock ($fp, LOCK_EX)
// записываем в файл-базу данные из выше
// прочитанного file_base.dat - переменна€ $f
// и добавл€ем еще одну строку с данными нового пользовател€
// снимаем блокировку
@flock ($fp, LOCK_UN)
// закрываем файл-базу
@fclose ($fp);
ЕЕ
?>

¬роде бы все нормально.  огда 1 пользователь начал записывать свои данные в file_base.dat, 2 "спит" одну секунду, ожида€ разблокировки file_base.dat, когда file_base.dat разблокирован, он считывает из него информацию и начинает движение дальше. Ќо, есть одно "но". ≈сли 2 пользователь чуть быстрее 1 пользовател€ "добежал" до "своего" места, прочитал половину данных из нашего file_base.dat, который еще не был блокирован, и, именно, в этот момент, 1 "добежал" по тексту скрипта до @flock ($fp, LOCK_EX), то есть заблокировал наш файл, то переменна€ $fp будет иметь только половину данных из нашего file_base.dat, потому что, именно, в этот момент file_base.dat был заблокирован. ¬ итоге, в наш file_base.dat будет записана половина информации из нашей базы + одна сторка нового пользовател€. “о есть, оп€ть, file_base.dat мы потер€ли.

—уть подхода, который предлагаем мы дл€ решени€ этой проблемы такой:
¬о врем€ считывани€ из файла или записи в него данных, на сайте по€вл€етс€ текстова€ строка с названием этого файла, когда из файла информаци€ прочиталась или записалась, эта текстова€ строка удал€етс€. ѕока она есть, значит кто-то уже использует базу данных на чтение или запись, пользователь, который хочет обратитьс€ к этой базе на чтение или запись, находитс€ в цикле, ожида€ пока текстова€ строка исчезнет.  ак только она исчезает, он читает или пишет в базу данных, сам при этом создава€ такую же текстовую строку, тем самым преп€тству€ доступу двух и более пользователей к базе. 

ƒл€ чего, на сайте создадим папку дл€ хранени€ текстовых строк. Ќапример, lock. Ќаш файл на сайте лежит в папке basedir, значит в папке lock надо создать папку basedir. “еперь, когда пользователь обращаетс€ к file_base.dat, дл€ чтени€ или записи в него, абсолютный путь https://наш_сайт.ru/basedir/file_base.dat, в папке https://наш_сайт.ru/lock/basedir/ по€вл€етс€ файл-строка file_base.dat.tmp, абсолютный путь - https://наш_сайт.ru/lock/basedir/file_base.dat.tmp, закрыва€ доступ к file_base.dat, как только пользователь считал или записал информацию в наш файл-базу, текстова€ строка  file_base.dat.tmp удал€етс€, открыва€ доступ другим пользовател€м к file_base.dat.

<?
// объ€вл€ем директорию дл€ временных файлов
$Lock_dir = "lock";
// функци€ дл€ создани€ временных текстовых файлов
function touchString($file) {
global $Lock_dir;
$tmp = "$Lock_dir/".$file.".tmp";
while(1) {
if (is_file($tmp))
{
while(file_exists($tmp))
{
$file_exist++;
if($file_exist > 10){break;}
clearstatcache();
sleep(1);
}
}
return touch($tmp);
}
}
// функци€ удал€юща€ временные текстовые файлы
function delString($file) {
global $Lock_dir;
$tmp = "$Lock_dir/".$file.".tmp";
return unlink($tmp);
}
// альтернатива функци @file
function FileArray($file) {
if (!is_readable($file)) return FALSE;
touchString($file);
$bufer = file($file);
delString($file);
return $bufer;
}
// альтернатива функци @fopen
function OpenFile($file, $mode) {
touchString($file);
return fopen($file, $mode);
}
// альтернатива функци @fclose
function CloseFile($fido, $file) {
$sito = fclose($fido);
delString($file);
return $sito;
}
ЕЕ
// провер€ем есть ли временный файл, запрещающий
// чтение и запись в файл-базу, если он есть, останавливаем
// пользовател€ в цикле, где он ожидает пока временный файл
// исчезнет, как толко он исчезает, сами создаем такой же временный
// файл, запреща€ доступ к файлу-базе, только после этого читаем данные из
// файла-базы, удал€ем временный файл, открыва€ доступ к базе
// другим пользовател€м
$f = FileArray ("basedir/file_base.dat"); // здесь находитс€ 2 пользователь
// здесь идет текст скрипта
// провер€ем есть ли временный файл, запрещающий
// чтение и запись в файл-базу, если он есть, останавливаем
// пользовател€ в цикле, где он ожидает пока временный файл
// исчезнет, как толко он исчезает, сами создаем такой же временный
// файл, запреща€ доступ к файлу-базе, только после этого открываем
// файл-базу на запись, временный файл не удал€етс€, закрыва€ доступ к базе
// другим пользовател€м
$fp = OpenFile ("basedir/file_base.dat", "w"); // здесь находитс€ 1 пользователь
// записываем в файл-базу данные из выше
// прочитанного basedir/file_base.dat - переменна€ $f
// и добавл€ем еще одну строку с данными нового пользовател€
// закрываем файл-базу, удал€ем временный файл, открыва€ доступ к базе
// другим пользовател€м
CloseFile ($fp, "basedir/file_base.dat");
// обратите внимание, что в CloseFile два аргумента: $fp и
// basedir/file_base.dat
ЕЕ
?>

¬ этой ситуации мы видим, что когда один пользователь достиг точки считывани€ с file_base.dat или, наоборот, другой пытаетс€ записать в file_base.dat информацию, нам ничего не страшно. ѕотому что, как в одном, так и в другом случае, по€вилс€ маленький текстовый файл file_base.dat.tmp, который не дает ни одному ни другому совместно читать или писать в file_base.dat.

¬ нашем варианте есть одна "дыра", если вдруг пользователь обратилс€ к базе данных, создав текстовую строку блокировки, незакончил как-бы цикл (свет погас и компьютер выключилс€), и эта текстова€ блокирующа€ строка осталась лежать в папке Lock, то наша программа "подвиснет", не дава€ никому пройти, из-за этого оставшегос€ флага. ћы не стали усложн€ть функции проверкой на удаление этого файла-флага, а, просто ввели в функции его создающей, предел "подвисани€" компьютера 10 секунд ($file_exist++; if($file_exist > 10){break;}), через 10 секунд он автоматически выйдет из цикла и сотрет временный файл. ¬ этом кроетс€ опасность, но она ничтожна мала по сравнению с теми, которые были описаны выше. —читаем, что подход описанный выше, защитит от обвала базы данных, котора€ лежит в текстовом файле и не будет особо заметно флагов блокировки, когда на сайте ее, одновременно использует до 10 человек. ћы знаем, что наш скрипт сервером исполн€етс€ 0,5 сек, в этом случае его пропускна€ способность в час составит до 1800 человек. ”меньшим ее вдвое, 900 человек, за сутки 21600 человек. —огласитесь, и без MySql можно обойтись.

ƒл€ проверки работоспособности этих функций запишите вручную в папку Lock этот самый блокирующий файл file_base.dat.tmp, в нашем случае, это: https://наш_сайт.ru/lock/basedir/file_base.dat.tmp, тем самым, блокиру€ доступ к базе https://наш_сайт.ru/basedir/file_base.dat, запустите программу, обратитесь к базе, вы увидите, что флаг работает и броузер "стоит" на месте, удалите file_base.dat.tmp файл (разблокиру€) и подвисание закончитс€.

“еперь у нас есть функции:
1. FileArray , замен€юща€ @file
2. OpenFile , замен€юща€ @fopen
3. CloseFile , замен€юща€ @fclose



јлександр ”стюгов
https://omsk-777.ru/



ѕри перепечатке информации делайте, пожалуйста, ссылку на наш сайт (либо поделитьс€ вконтакте, гугл+ или делитесь в одноклассниках статьЄй). —пасибо!

»сточник: www.wr-script.ru©, 2004-2023г.

ƒелитесь с друзь€ми в соцсет€х. јктивные комментаторы получают свежие скрипты и бесплатные доработки от WR-Script.ru!

¬ернутьс€ к стать€м помощи WEB-мастеру

WR-–°—З—С—В—З–Є–Ї