Реализация файла очереди PHP

#php #file #unix #memory #queue

#php #файл #unix #память #очередь

Вопрос:

Для проекта, над которым я работал, мне нужна очередь, которая будет слишком большой для хранения в обычной памяти. Я реализовывал его как простой файл, в котором он считывал весь файл целиком, брал первые несколько (~ 100) строк, обрабатывал их, затем записывал обратно обновленную очередь с добавлением новых инструкций и удалением старых. Однако, поскольку очередь стала слишком большой для хранения в памяти, мне нужно что-то другое. Желательно, чтобы кто-нибудь мог подсказать мне способ удалить только первые несколько строк файла без необходимости просматривать остальные данные. Я думал об использовании базы данных (MySQL, вероятно, с отсортированными временными метками вставки), но я бы предпочел сделать это без учета нагрузки и пропускной способности (несколько серверов должны были бы отправлять и получать много данных из БД).). Язык, на котором я работаю, — PHP, но на самом деле этот вопрос больше касается файлов unix, я полагаю. Любая помощь будет оценена.

Комментарии:

1. » несколько серверов должны были бы отправлять и получать много данных из БД » — это именно то, для чего предназначены (и оптимизированы) серверы баз данных.

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

Ответ №1:

Извлечение первой строки файла довольно тривиально ( fopen() за ним следует fgets() ). Перезапись файла для удаления завершенных заданий будет очень болезненной, особенно если у вас есть несколько одновременных серверов, работающих с одним и тем же файлом очереди.

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

Комментарии:

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

2. вы могли бы использовать ftell() , чтобы получить позицию маркера «начать здесь» в файле очереди и сохранить его в отдельном файле. блокировка / чтение / обновление / запись / разблокировка небольшого ~ 10-байтового файла будет довольно быстрой и позволит вам легко перейти непосредственно к нужному месту в очереди.

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

Ответ №2:

У меня были те же проблемы, когда я работал над транспортом в очереди / fs. Мне не удалось изменить небольшую часть в начале файла, не скопировав его в память и не сохранив обратно. Вместо этого, но это возможно сделать с концом файла. Вы можете прочитать часть, а затем обрезать ее. На самом деле это не очередь, а стек. Так что, если вы полагаетесь на упорядочение сообщений, это не будет решением. В моем случае я блокирую файл, когда файл был прочитан из файла, блокировка снимается.

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

 <?php
$rawMessage = 'this your message to put to the queue as a string';

$queueFile = fopen('/path/to/queue/file', ' a');

// here it may add some spaces so the message length is multiples of modular.
// that make it easier to read messages from a file.

// lock file

$rawMessage = str_repeat(' ', 64 - (strlen($rawMessage) % 64)).$rawMessage;
fwrite($queueFile, $rawMessage);

// release lock
  

Вот как вы могли бы читать сообщения из файла очереди:

 <?php

$queueFile = fopen('/path/to/queue/file', ' c');

// lock file

$frame = readFrame($file, 1);
ftruncate($file, fstat($file)['size'] - strlen($frame));
rewind($file);
$rawMessage = substr(trim($frame), 1);

// release lock


function readFrame($file, $frameNumber)
{
    $frameSize = 64;
    $offset = $frameNumber * $frameSize;
    fseek($file, -$offset, SEEK_END);
    $frame = fread($file, $frameSize);
    if ('' == $frame) {
        return '';
    }
    if (false !== strpos($frame, '|{')) {
        return $frame;
    }
    return readFrame($file, $frameNumber   1).$frame;
}
  

Для блокировки я бы предложил использовать Symfony LockHandler или просто поставить в очередь / fs.