Корректное использования flock() в PHP
"Arsen Kirillov" <Arsen.Kirillov@p39.f90.n462.z2.fidonet.org> wrote in message news:1010705361@p39.f90.n462.z2.ftn...
> VK> Так. Hо маловероятно..:)
Всё зависит от траффика. Уже при обращениях около сотни в день будут конфликты такого рода каждые несколько часов.
> Я бы рекомендовал использовать самодельные семафоры типа есть файл/нет файла.
> ИМХО самый лучший способ а про флок даже в доке говорят написано :
В общем, делюсь своими проблемами и как я их решил
Я мучался с этим где-то около года (эта эха должна помнить мои неоднократные вопли о помощи). Сперва со счётчиками посещений - но это ещё как-то не страшно было - осыпалось и осыпалось... Потом с рейтингом сайтов. Вот тут уже начались проблемы. Самые популярные участники рейтинга дают более 3000 показов счётчика (а значит и обновлений статистики) в сутки. Всего же их более сотни.
Файлы-семафоры немного спасали. Но, во-первых, тормозили мне работу, во-вторых, случались регулярные dead-lock'и.
А решилось всё штатным flock()'ом.
Сперва преамбула. Напомню формат использования:
$fh=fopen(...);
flock($fh,2);
... // работаем с файлом
fclose($fh); // flock перед fclose не обязателен.
В идеале работает так, что если открываем незаблокированный файл, то всё ок, если заблокированный - то ждём, пока освободится.
Реально что происходит: открываем незаблокированный файл. И тут, в момент, пока уже сработал fopen(), но ещё не сработал flock() срабатывает fopen второго скрипта! Всё, конфликт доступа. Каждый пишет что-то своё, счётчики слетают. Это не маловероятное событие! При ~1000 запусков скрипта в сутки сбои случаются каждые 2-3 дня!
Решение простое. Файл нужно открывать так, чтобы открытием не убивать его содержимое! Т.е. не по 'w', а по 'a'. А потом уже использовать fseek и ftruncate. Вот пример простого непадучего счётчика прямо из моей библиотеки. На входе идентификатор счётчика (обычно это URL страницы, если отсутствует, то берётся имя текущего скрипта.
Подразумевается, что каталог счётчиков - $DOCUMENT_ROOT/../cgi-bin/data/counts/
Каждый счётчик - отдельный файл, где записан адрес страницы (для рейтингов, скажем), значение счётчика, время запуска счётчика, и время последнего с предпоследним обращений.
function count_inc($id="")
{
global $PHP_SELF, $DOCUMENT_ROOT;
if(!$id)
$id=$PHP_SELF;
$idb=str_replace("/","#",substr($id,1));
$ids=str_replace("/","_",substr($id,1));
$ids=str_replace("?","_",$ids);
$ids=str_replace(":","_",$ids);
$cnt_file="$DOCUMENT_ROOT/../cgi-bin/data/counts/$ids.txt";
$fh=fopen($cnt_file,"a+");
flock($fh,2);
fseek($fh,0);
list($sid,$count,$date,$rlast,$clast)=split("\|",chop(fread($fh,9999)));
$count++;
if(!$date||!$count||!$rlast||!$clast||strftime("%Y",$date)<"2001")
{
if(!$ddate||!$dcount||!$drlast||!$dclast)
{
$count=1;
$date=time();
$rlast=time();
$clast=time();
}
else
{
$count=$dcount;
$date=$ddate;
$rlast=$drlast;
$clast=$dclast;
}
}
ftruncate($fh,0);
fwrite($fh,"$id|$count|$date|$clast|".time());
fclose($fh);
chmod($cnt_file,0666);
}
Т.е. делаем так:
$fh=fopen($file, "a+"); // это если ещё и читать надо.
// Если только писать - то достаточно "a"
flock($fh); // блокируем файл.
fseek($fh,0); // переходим в начало.
fread(...); // читаем, если надо.
...
ftruncate($fh,0); // режем/трём файл по нулям.
fwrite(...); // пишем что надо
fclose($fh);
Это работает без сбоев месяцами при тысячах обращений к файлу в день.
Следует также помнить, что читать файл нужно только через fopen() (можно fopen(..","r")) НО НЕ ЧЕРЕЗ file() или readfile(). Может считаться полузаписанный файл. Т.е. если это только для разового чтения и сбой не страшен, то пожалуйста. Если же требуется что-то прочитать, изменить и записать, то всё надо делать в одно открытие файла, как в приведённом выше примере.
--
...Глубина-глубина, я не твой...
=KRoN=, Создать сайт бесплатно, твоё-имя.da.ru! Сделай сайт и получи бесплатный домен (крутые бесплатные домены и хостинг, простой конструктор сайтов), 4886816
Комментарии
- Корректное использования flock() в PHP [=KRoN=#31.10.02 07:48]
- Корректное использования flock() в PHP [<#31.10.02 04:43]