Блокировка файлов NFS в PHP

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

flock(...) обычно превосходно, но он, похоже, не работает на NFS... Что является огромной проблемой для меня, поскольку производственный сервер использует массив NFS.

самое близкое, что я видел к реальному решению, - это попытка создать Lock dir и ждать, пока он не будет создан. Сказать, что этому не хватает элегантности, - это преуменьшение года, возможно, десятилетия.

идеи получше?

Edit: я должен добавить, что у меня нет root на сервере, и делать хранилище по-другому не очень возможно в ближайшее время, не в последнюю очередь в течение моего срока.

6 ответов


еще один грязный хак будет flock() "локальный" файл, и только открыть / записать в файл NFS, если вы держите блокировку на локальном файле.

Edit: из flock() страницы:

flock () не будет работать на NFS и многих другие сетевые файловые системы. Проверять документация по операционной системе более подробный.

Edit 2:

конечно, всегда используя базу данных synchonise доступ (я предполагаю, что ваше приложение использует децибел.) Это было бы довольно хитом производительности, если вы делаете много журналов, хотя.

Если это только для ведения журнала, вам действительно нужен централизованный файл журнала? Можете ли вы войти локально (и даже объединить журналы, когда они вращаются в конце дня, если это необходимо)?


операции каталога не atomic под NFSv2 и NFSv3 (пожалуйста, обратитесь к книге "NFS Illustrated" Брента Каллагана, ISBN 0-201-32570-5; Brent is a NFS-veteran at Sun).

NFSv2 имеет две атомарные операции:

  • ссылка
  • переименовать

С NFSv3 создать и атомную.

зная это, вы можете реализовать спин-блокировки для файлов и каталоги (в оболочке, не PHP):

lock current dir:

while ! ln -s . lock; do :; done

заблокировать файл:

while ! ln -s ${f} ${f}.lock; do :; done 

разблокировать (предположение, запущенный процесс действительно приобрел блокировку):

разблокировать текущий реж:

mv lock deleteme && rm deleteme

открыть файл:

mv ${f}.lock ${f}.deleteme && rm ${f}.deleteme

Remove также не является атомарным, поэтому сначала переименуйте (который является атомарным), а затем удалить.

для вызовов symlink и rename оба имени файлов должны находиться на та же файловая система. Мое предложение: использовать только простые имена файлов и put файл и блокировка в одном каталоге.


одним из подходов может быть настройка memcache для экземпляр, разделяемый между каждым из ваших виртуальных серверов. Ты можешь обезьянничать!--1--> помещая запись имени файла в кэш при запуске локальных файловых операций и стирая ее по завершении.

каждый сервер может получить доступ к этому пулу перед файловой операцией и посмотреть, присутствует ли эта "блокировка", например

// Check for lock, using $filename as key
$lock = $memcache->get($filename);

if(!$lock) {
    // Set lock in memcache for $filename
    $memcache->set($filename, 1);

    // Do file operations...

    // Blow away "lock"
    $memcache->delete($filename);
}

не самое элегантное из решений, но должно позволить вам контролировать замки от всех серверы в вашей настройке с относительной легкостью.


вы также можете использовать dio_fcntl() для блокировки файлов на томах NFS. Для этого требуется пакет dio, который по умолчанию может не быть частью вашей установки php.


хотя вы не можете flock () файлы на NFS и I / O могут быть асинхронными, операции с каталогами на NFS are атомные. Это означает, что в любой момент времени каталог существует или не существует.

чтобы реализовать свою собственную функцию блокировки NFS, проверьте или создайте каталог, когда вы хотите его заблокировать, и удалите его, когда закончите.

к сожалению, он, вероятно, не совместим с любым другим приложением, которое вы не написали сами.


следует просто использовать memcache add и избегать состояния гонки.

if ($memcache->add($filename, 1, 1))
{
   $memcache->delete($filename);
}