Подтвердить что ты не робот

Самый быстрый способ сравнить состояние каталога или хеширование для удовольствия и прибыли

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

Мы придумали это, что является самым быстрым, что нам удалось ускорить, и на среднем уровне 0,08 на машине разработчика для типичного проекта. Мы экспериментировали с shasum, md5 и crc32, и это самый быстрый. Мы в основном собираем содержимое каждого файла, а md5 - вывод. Безопасность не вызывает беспокойства, нас просто интересует обнаружение изменений файловой системы с помощью различной контрольной суммы.

time (find application/ -path '*/.svn' -prune -o -type f -print0 | xargs -0 md5 | md5)

Я полагаю, вопрос в том, может ли это быть оптимизировано еще?

(Я понимаю, что обрезка svn будет стоить, но находка занимает наименьшее количество времени из компонентов, поэтому она будет довольно минимальной. Мы тестируем это на рабочей копии atm)

4b9b3361

Ответ 1

Мы не хотели использовать FAM, так как нам нужно было установить его на сервер, и это не всегда возможно. Иногда клиенты настаивают на развертывании на их сломанной инфраструктуре. Поскольку это прекращено, его трудно получить, что изменение также одобрило волокиту.

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

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

Отметьте это как "ответ" и подтвердите ответ FAM.

Ответ 2

Вы можете получать уведомления об изменениях файловой системы, используя расширение inotify.

Его можно установить с помощью pecl:

pecl install inotify

Или вручную (загрузить, phpize &./configure & make && make install как обычно).

Это необработанное привязку к системным вызовам linux inotify и, вероятно, является самым быстрым решением для Linux.

См. этот пример простой реализации tail: http://svn.php.net/viewvc/pecl/inotify/trunk/tail.php?revision=262896&view=markup


Если вы хотите, чтобы библиотека более высокого уровня или суппот для систем, отличных от Linux, взгляните на Lurker.

Он работает в любой системе и может использовать inotity, когда он доступен.

См. пример из README:

$watcher = new ResourceWatcher;
$watcher->track('an arbitrary id', '/path/to/views');

$watcher->addListener('an arbitrary id', function (FilesystemEvent $event) {
    echo $event->getResource() . 'was' . $event->getTypeString();
});

$watcher->start();

Ответ 3

Вместо того, чтобы идти по содержимому файла, вы можете использовать ту же технику с именем файла и временными метками:

find . -name '.svn' -prune -o -type f -printf '%m%c%p' | md5sum

Это намного быстрее, чем чтение и хэширование содержимого каждого файла.

Ответ 4

Вместо активного поиска изменений, почему бы не получить уведомление, когда что-то изменится. Посмотрите PHP FAM - API монитора изменения файлов

FAM контролирует файлы и каталоги, уведомляя о заинтересованных приложениях изменений. Более подробную информацию о FAM можно получить по адресу http://oss.sgi.com/projects/fam/. PHP скрипт может указывать список файлов для FAM для мониторинга с помощью функций, предоставляемых этим расширением. Процесс FAM запускается при открытии первого подключения из любого приложения к нему. Он выходит после того, как все соединения с ним были закрыты.

CAVEAT: требуется дополнительный демон на машине, а расширение PECL не поддерживается.

Ответ 5

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

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

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

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

Я честно считаю, что лучший способ в этом случае - просто использовать уже доступные инструменты.

инструмент linux diff имеет параметр -q (быстрый).

вам нужно будет использовать его с рекурсивным параметром -r.

diff -r -q dir1/ dir2/

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

Ответ 6

Определенно, что вы должны использовать, Inotify его быстро и легко настроить, несколько опций непосредственно из bash или php посвятить простой node-inotify экземпляр для этой задачи

Но Inotify не носится на окнах, но вы можете легко написать приложение командной строки с FileSystemWatcher или FindFirstChangeNotification и вызов через exec

Если вы ищете только PHP solution, то это довольно сложно, и вы не можете получить производительность, потому что единственный способ - непрерывно сканировать эту папку

Вот Простой эксперимент

  • Не использовать в производстве
  • Невозможно управлять большим файловым набором
  • Не поддерживает мониторинг файлов
  • Поддержка только NEW, DELETED и MODIFIED
  • Не поддерживает рекурсию

Пример

if (php_sapi_name() !== 'cli')
    die("CLI ONLY");

date_default_timezone_set("America/Los_Angeles");

$sm = new Monitor(__DIR__ . "/test");

// Add hook when new files are created
$sm->hook(function ($file) {
    // Send a mail or log to a file
    printf("#EMAIL NEW FILE %s\n", $file);
}, Monitor::NOTIFY_NEW);

// Add hook when files are Modified
$sm->hook(function ($file) {
    // Do monthing meaningful like calling curl
    printf("#HTTP POST  MODIFIED FILE %s\n", $file);
}, Monitor::NOTIFY_MODIFIED);

// Add hook when files are Deleted
$sm->hook(function ($file) {
    // Crazy ... Send SMS fast or IVR the Boss that you messed up
    printf("#SMS DELETED FILE %s\n", $file);
}, Monitor::NOTIFY_DELETED);

// Start Monitor
$sm->start();

Используемый кэш

// Simpe Cache
// Can be replaced with Memcache
class Cache {
    public $f;

    function __construct() {
        $this->f = fopen("php://temp", "rw+");
    }

    function get($k) {
        rewind($this->f);
        return json_decode(stream_get_contents($this->f), true);
    }

    function set($k, $data) {
        fseek($this->f, 0);
        fwrite($this->f, json_encode($data));
        return $k;
    }

    function run() {
    }
}

Класс экспериментов

// The Experiment
class Monitor {
    private $dir;
    private $info;
    private $timeout = 1; // sec
    private $timeoutStat = 60; // sec
    private $cache;
    private $current, $stable, $hook = array();
    const NOTIFY_NEW = 1;
    const NOTIFY_MODIFIED = 2;
    const NOTIFY_DELETED = 4;
    const NOTIFY_ALL = 7;

    function __construct($dir) {
        $this->cache = new Cache();
        $this->dir = $dir;
        $this->info = new SplFileInfo($this->dir);
        $this->scan(true);
    }

    public function start() {
        $i = 0;
        $this->stable = (array) $this->cache->get(md5($this->dir));

        while(true) {
            // Clear System Cache at Intervals
            if ($i % $this->timeoutStat == 0) {
                clearstatcache();
            }

            $this->scan(false);

            if ($this->stable != $this->current) {
                $this->cache->set(md5($this->dir), $this->current);
                $this->stable = $this->current;
            }

            sleep($this->timeout);
            $i ++;

            // printf("Memory Usage : %0.4f \n", memory_get_peak_usage(false) /
            // 1024);
        }
    }

    private function scan($new = false) {
        $rdi = new FilesystemIterator($this->dir, FilesystemIterator::SKIP_DOTS);

        $this->current = array();
        foreach($rdi as $file) {

            // Skip files that are not redable
            if (! $file->isReadable())
                return false;

            $path = addslashes($file->getRealPath());
            $keyHash = md5($path);
            $fileHash = $file->isFile() ? md5_file($path) : "#";

            $hash["t"] = $file->getMTime();
            $hash["h"] = $fileHash;
            $hash["f"] = $path;

            $this->current[$keyHash] = json_encode($hash);
        }

        if ($new === false) {
            $this->process();
        }
    }

    public function hook(Callable $call, $type = Monitor::NOTIFY_ALL) {
        $this->hook[$type][] = $call;
    }

    private function process() {
        if (isset($this->hook[self::NOTIFY_NEW])) {
            $diff = array_flip(array_diff(array_keys($this->current), array_keys($this->stable)));
            $this->notify(array_intersect_key($this->current, $diff), self::NOTIFY_NEW);
            unset($diff);
        }

        if (isset($this->hook[self::NOTIFY_DELETED])) {
            $deleted = array_flip(array_diff(array_keys($this->stable), array_keys($this->current)));
            $this->notify(array_intersect_key($this->stable, $deleted), self::NOTIFY_DELETED);
        }

        if (isset($this->hook[self::NOTIFY_MODIFIED])) {
            $this->notify(array_diff_assoc(array_intersect_key($this->stable, $this->current), array_intersect_key($this->current, $this->stable)), self::NOTIFY_MODIFIED);
        }
    }

    private function notify(array $files, $type) {
        if (empty($files))
            return;

        foreach($this->hook as $t => $hooks) {
            if ($t & $type) {
                foreach($hooks as $hook) {
                    foreach($files as $file) {
                        $info = json_decode($file, true);
                        $hook($info['f'], $type);
                    }
                }
            }
        }
    }
}