Как мне повторить попытку PHP flock () в течение определенного периода времени?

Мне нужно открыть файл журнала для записи. Проблема в том, что многое может сделать это одновременно, и я не хочу конфликтов. Каждая запись будет одной строкой, обычно около 150 байт (и всегда меньше 1K), а получение вещей в хронологическом порядке строго не требуется.

Я думаю, что я хочу, чтобы пытаться flock() , и если это не удается, продолжайте пробовать несколько секунд. Если блокировка не может быть установлена ​​после нескольких попыток, то сдайтесь.

 $fh=fopen($logfile, "a"); if (flock($fh, LOCK_EX|LOCK_NB)) { $locked=TRUE; } else { $locked=FALSE; // Retry lock every 0.1 seconds for 3 seconds... $x=0; while($x++ < 30) { usleep(100000); if (flock($fh, LOCK_EX|LOCK_NB)) { $locked=TRUE; break; } } } if ($locked) { if (fwrite($fh, strftime("[%Y-%m-%d %T] ") . $logdata . "\n")) { print "Success.\n"; } else { print "Fail.\n"; } flock($fh, LOCK_UN) } else { print "Lock failed.\n"; } 

У меня есть два вопроса : один общий и один конкретный. Во-первых, помимо реализации одного и того же решения по-разному ( do...while и т. Д.), Существует ли лучшая общая стратегия для решения этой проблемы, которая выполняется исключительно на PHP? Во-вторых, есть ли лучший способ реализовать это в PHP? (Да, я отделил их, потому что меня действительно интересует часть стратегии.)

Один из вариантов, который я рассмотрел, – использовать syslog () , но PHP-код, возможно, придется запускать на платформах, где администрирование на системном уровне (т.е. добавление вещей в /etc/syslog.conf) может быть недоступно в качестве опции.

UPDATE: добавлено |LOCK_NB в код выше, за предложение randy .

В моем многолетнем опыте создания журналов PHP (под Linux!) Я никогда не сталкивался с проблемами конфликтов (даже с сотнями одновременных и параллельных записей). Так простое управление блокировкой пропуска:

 $fh=fopen($logfile, "a"); if (fwrite($fh, strftime("[%Y-%m-%d %T] ") . $logdata . "\n")) { print "Success.\n"; } else { print "Fail.\n"; } fclose($fh); 

Об этой стратегии ведение журнала файла (с блокировкой или без него) не лучшее решение, потому что каждый fopen с « a » подразумевает, что syscall задает курсор в конце файла. syslog, сохраняя файл открытым, избегает этих накладных расходов.

Конечно, накладные расходы становятся значимыми (по производительности) с «большими» файлами, простым решением является создание файла журнала с датой (или датой) в названии.

ДОБАВИТЬ

Пакет apache включает программу тестеров: ab , позволяющую выполнять запрос в параллелизм, вы можете проверить мой тезис, подчеркивая ваш сервер с 1000000 запросов, сделанных потоками 10to1000.

ДОБАВИТЬ – после комментария

Нет, это непростая задача.

Я нашел заметку из http://php.net/manual/en/function.fwrite.php

Если дескриптор был fopen () ed в режиме добавления, fwrite () s являются атомарными (если размер строки не превышает размер блока файловой системы на некоторых платформах и пока файл находится в локальной файловой системе). То есть, нет необходимости ставить () ресурс перед вызовом функции fwrite (); все данные будут записаны без перерывов.

знать, насколько велик блок в байтах (обычный 4k):

dumpe2fs / dev / sd_your_disk_partition | less -i

«Атомность» записи реализована, блокируя другие «агенты» для записи (когда вы видите процесс в статусе «D» в «ps ax»), BUT PHP- поток может решить эту проблему, см.: * Stream_set_blocking *. Этот подход может вводить частичную запись, поэтому вам придется проверять целостность ваших записей.

В любом случае fwrite (сеть или файл) подвержен блокировке / сбою, независимо от использования стаи . ИМХО стадо вводит только накладные расходы.

О ваших оригинальных вопросах и вашей цели (попытка внедрить корпоративную политику в среде с высоким риском), где даже fwrite может быть проблематичным, я могу только представить себе простое решение: использовать БД

  • большая часть сложности из PHP и написана на C!
  • полное управление потоком операции
  • hi уровень параллелизма