Информация

Сайт в стадии реконструкции. Пользуйтесь форумами.

Твиттер

Статистика

Яндекс.Метрика
Друзья, соседи, проекты

Корректное использования 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