Мне нужно открыть файл журнала для записи. Проблема в том, что многое может сделать это одновременно, и я не хочу конфликтов. Каждая запись будет одной строкой, обычно около 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 может быть проблематичным, я могу только представить себе простое решение: использовать БД