блокировка файлов fopen в PHP (тип ситуации reader/writer)


У меня есть сценарий, в котором один процесс PHP пишет файл примерно 3 раза в секунду, а затем несколько процессов PHP читают этот файл.

Этот файл по сути является кэшем. Наш веб-сайт имеет очень настойчивый опрос, для данных, которые постоянно меняются, и мы не хотим, чтобы каждый посетитель попадал в БД каждый раз, когда они опрашивают, поэтому у нас есть процесс cron, который читает БД 3 раза в секунду, обрабатывает данные и сбрасывает их в файл, который затем могут прочитать клиенты опроса.

Проблема, с которой я сталкиваюсь, заключается в том, что иногда открытие файла для записи в него занимает много времени, иногда даже до 2-3 секунд. Я предполагаю, что это происходит, потому что он блокируется reads (или чем-то еще), но у меня нет никакого убедительного способа доказать, что, кроме того, согласно тому, что я понимаю из документации, PHP не должен ничего блокировать. Это происходит каждые 2-5 минут, так что это довольно распространенное явление.

В коде я не делаю никакой блокировки, и я в значительной степени не волнует, если информация этого файла будет повреждена, если чтение завершится неудачей или если данные изменятся в середине чтения. Однако меня волнует, если запись к нему занимает 2 секунды, эзотерически, потому что процесс, который должен происходить трижды в секунду, теперь пропустил несколько ударов.

Я пишу файл с этим кодом:

$handle = fopen(DIR_PUBLIC . 'filename.txt', "w");
fwrite($handle, $data);
fclose($handle);

И я читаю его непосредственно с:

file_get_contents('filename.txt')

(он не подается непосредственно клиентам в виде статического файла, я получаю обычный запрос PHP который читает файл и делает с ним некоторые основные вещи)

Размер файла составляет около 11 КБ, поэтому чтение/запись не занимает много времени. Хорошо под 1ms.

Это типичная запись в журнале, когда возникает проблема:

  Open File:    2657.27 ms
  Write:    0.05984 ms
  Close:    0.03886 ms

Не уверен, что это актуально, но чтение происходит в обычных веб-запросах, через apache, но запись-это обычное выполнение PHP "командной строки", выполняемое cron Linux, оно не проходит через Apache.

Любые идеи о том, что может быть причиной такой большой задержки в открыть файл?
Какие-нибудь указания на то, где я мог бы искать, чтобы помочь мне определить истинную причину?

В качестве альтернативы, можете ли вы придумать что-то, что я мог бы сделать, чтобы избежать этого? Например, я хотел бы иметь возможность установить тайм-аут 50 МС на fopen, и если он не открывает файл, он просто пропускает вперед и позволяет следующему запуску cron позаботиться об этом.

Опять же, мой приоритет-держать хрон бьющимся три раза в секунду, все остальное вторично, поэтому любые идеи, предложения, что-либо чрезвычайно важно. добро пожаловать.

Спасибо!
Даниил

2 4

2 ответа:

Я могу придумать 3 возможных проблемы:

  • файл блокируется при чтении / записи в него более низкими системными вызовами php без вашего ведома. Это должно заблокировать файл на 1/3s максимум. У вас бывают периоды длиннее этого.
  • кэш fs запускает функцию fsync() , и вся система блокирует чтение / запись на диск, пока это не будет сделано. В качестве исправления вы можете попробовать установить больше оперативной памяти или обновить ядро или использовать более быстрый жесткий диск
  • ваше решение "кэширования" не распространяется, и оно бьет по самому худшему действующему аппаратному элементу во всей системе много раз в секунду... это означает, что вы не можете масштабировать его дальше, просто добавив больше машин, только увеличив скорость жесткого диска. Вы должны взглянуть на memcache или APC, или, возможно, даже на общую память http://www.php.net/manual/en/function.shm-put-var.php

Решения, которые я могу придумать:

  • поместите этот файл в рамдиск http://www.cyberciti.biz/faq/howto-create-linux-ram-disk-filesystem/ . Это должно быть самым простым способом, чтобы избежать попадания на диск так часто, без других серьезных изменений.
  • Используйте memcache. Его очень быстро используют локально, он хорошо масштабируется, используется "крупными" игроками. http://www.php.net/manual/en/book.memcache.php
  • Используйте общую память. Он был разработан для того, что вы пытаетесь сделать здесь... наличие "зоны общей памяти"...
  • Измените планировщик cron, чтобы обновлять меньше, возможно, реализовать какую-то систему событий, так что он будет обновлять только кэш когда это необходимо, а не на временной основе
  • Сделайте так, чтобы сценарий "запись" записывался в 3 разных файла, а" читатели " читали из одного файла случайным образом. Это может позволить "распределить" loking по большему количеству файлов, и это может уменьшить вероятность того, что определенный файл будет заблокирован при записи в него... но я сомневаюсь, что это принесет какую-то заметную пользу.

Вы должны использовать что-то Действительно быстрое решение, если вы хотите гарантировать постоянное низкое время открытия. Возможно, ваша ОС выполняет синхронизацию дисков, фиксацию файлов базы данных или другие вещи, которые вы не можете обойти.

Я предлагаю использовать memcached, redis или даже mongoDB для таких задач. Вы можете даже написать свой собственный демон кэширования, даже на php (однако это совершенно не нужно и может быть сложно).

Если вы абсолютно, положительно уверены, что вы можете решить эту задачу только путем этот файловый кэш, и вы находитесь под Linux, попробуйте использовать другой дисковый планировщик ввода-вывода, например deadline, или (cfq и уменьшить приоритет процесса PHP до -3 / -4).