[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Потоковое чтение из файла
Kolgarn
Собственно имеется файл со строками:

Цитата
[07:07:59.503] [SomeBody {123445}:123] [Something {3213}] [Here you are] [oO Kill:0 {123df}] ()



В этот файл постоянно(порой по 5-10 строк в секунду) сторонняя программа пишет подобные строки.
Задача прочитать, обработать и выдать результат проанализировав текст.

На данный момент чтение из файла реализовано как:

$handle = fopen($file,'r');
do
{
unset($data,$string);
$string = trim(stream_get_line($handle, 10000,"\n"));
if($string) $data = ParsString($string);
if(!empty($data)) { /* Добавляем текст */ }
if(!$string) delay($config['timeout']);
}
while(true);

Нет, оно не падает и етк. Вопрос лишь в том, что я совсем не уверен в разумности такого метода безостановочного чтения. Неужели не существует метода проверить наличия новой строки не вызывая нагрузку, которая превзойдет постоянное повторное чтение?

Пробовал с filesize, но как результат увеличение времени обработки в 10 раз, что не приемлемо.
Feof в моем случае почему-то по достижению конца единожды попросту переставал работать.

Собственно 2 вопроса:
1) Может есть более разумный способ читать данные из файла?
2) Каким образом на ваш взгляд можно удобнее и быстрее разбирать указанную строку на составляющие?

Так же заметил особенность при такой реализации - оно порой попросту пропускает последн(ие)юю строку, если одновременно много записалось.

Ну и дополнительный момент в том, что критичен порядок обработки ибо в строках передаются действия объектов постоянно. Объекты разные и воздействие идет с другими объектами.
Так же момент в том, что строки все разные и передается уникальный идентификатор объекта, который переодически меняется + строк огромное кол-во, что не позволяет читать файл каждый раз + критично время от записи в файл до обработки.



Спустя 1 час, 12 минут, 30 секунд (23.04.2012 - 12:26) I++ написал(а):
CS 1.6 чтоли? Если да, намного проще на pawn написать сбор подобной статистики.

Цитата
Неужели не существует метода проверить наличия новой строки не вызывая нагрузку, которая превзойдет постоянное повторное чтение?


Это называется long polling, в php он отсутствует.
-------------

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

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

Файлы очень медленная хренотень, лучше использовать fifo либо Shared Memory and IPC. Либо писать свой протокол синхронизации и передачи данных через shmop и сигналы pctl, скорость возростет до несколько гигабайт в секунду.

Вот пример не блокирующего чтения с применением эвентов (эмуляция полинга)

<?php

class
SHMOP
{
private static $shmop_key;
private static $PIPES;
private static $process;
private static $header_size = 6; // Max 999 999 bytes recv
private static $event_stdio;
private static $event_sigio;
private static $pid;

public static function init($type, $fullpath, $memory = null, &$base)
{
if($type === 'client')
{
if(!is_numeric($memory))
return false;

$descriptorspec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"));
self::$process = proc_open('/usr/local/bin/php -f '.$fullpath, $descriptorspec, self::$PIPES);

if(!is_resource(self::$process))
return false;

if(!self::open($fullpath, 'c', 0644, $memory))
return false;

self::$event_stdio = event_new();
// костыль с SHMOP::stdio_command (Подумай как исправить)
event_set(self::$event_stdio, self::$PIPES[1], EV_READ | EV_PERSIST, __CLASS__.'::stdio_command');
event_base_set(self::$event_stdio, $base);
event_add(self::$event_stdio);

self::$event_sigio = event_new();
event_set(self::$event_sigio, SIGIO, EV_SIGNAL | EV_PERSIST, "IO_signal");
event_base_set(self::$event_sigio, $base);
event_add(self::$event_sigio);

// Send my pid
fwrite(self::$PIPES[0], 'parent_pid:'.posix_getpid()."\n");

return true;
}
else if($type === 'server')
{
if(!self::open($fullpath, 'w'))
return false;

self::$event_stdio = event_new();
// костыль с SHMOP::stdio_command (Подумай как исправить)
event_set(self::$event_stdio, STDIN, EV_READ | EV_PERSIST, __CLASS__.'::stdio_command');
event_base_set(self::$event_stdio, $base);
event_add(self::$event_stdio);

self::$event_sigio = event_new();
event_set(self::$event_sigio, SIGIO, EV_SIGNAL | EV_PERSIST, "IO_signal");
event_base_set(self::$event_sigio, $base);
event_add(self::$event_sigio);

return true;
}
else
return
false;
}
public static function send_signal()
{
posix_kill(self::$pid, SIGIO);
}

public static function stdio_command($fd, $events)
{
$com = trim(fgets($fd));
$com_arr = explode(':', $com);

switch($com_arr[0])
{
case 'child_pid':
self::$pid = $com_arr[1];
break;

case 'parent_pid':
// Set parent pid
self::$pid = $com_arr[1];
// Send child pid
fwrite(STDOUT, 'child_pid:'.posix_getpid()."\n");
break;
}
}


public function destruct()
{
self::delete();
self::close();
}

public function close()
{
if(shmop_close(self::$shmop_key))
return true;
else
return
false;
}

private function delete()
{
if(shmop_delete(self::$shmop_key))
return true;
else
return
false;
}

private static function open($fullpath, $flags, $mode = 0, $block_size = 0)
{
// Add header size
$shmop_key = self::key($fullpath);

self::$shmop_key = shmop_open($shmop_key, $flags, $mode, $block_size);

if(!self::$shmop_key)
return false;

// Временный костыль
shmop_write(self::$shmop_key, "\0", 0);
return true;
}

public static function read()
{
// Check SpinLock
$command = shmop_read(self::$shmop_key, 0, 1);

if($command === "\0")
return '';

// Get Header
$datasize = (int)shmop_read(self::$shmop_key, 1, self::$header_size);

$data = shmop_read(self::$shmop_key, self::$header_size+1, $datasize);
// release spinlock
shmop_write(self::$shmop_key, "\0", 0);
return $data;
}

public static function canwrite()
{
// Check SpinLock
if(shmop_read(self::$shmop_key, 0, 1) === "\0")
return true;
else
return
false;
}

public static function write($data)
{
// Check SpinLock
$command = shmop_read(self::$shmop_key, 0, 1);

if($command !== "\0")
return false;

// Check datasize header lenght and add zero symbol.
$datasize = strlen($data);
if(strlen($datasize) < self::$header_size)
$datasize .= "\0";

// Write Header
shmop_write(self::$shmop_key, $datasize, 1);
// Write Data
shmop_write(self::$shmop_key, $data, self::$header_size+1);
// Write spinlock
shmop_write(self::$shmop_key, "\1", 0);
return true;
}

public static function feof()
{
// Check SpinLock
if(shmop_read(self::$shmop_key, 0, 1) === "\2")
return true;
else
return
false;
}

public static function sendeof()
{
// Check SpinLock
$command = shmop_read(self::$shmop_key, 0, 1);

if($command !== "\0")
return false;

shmop_write(self::$shmop_key, "\2", 0);
return true;
}

public static function key($fullpath = __FILE__)
{
return -934083471;
}
}


?>


<?php

//var_dump(ftok('proc_server.php', chr(200)));
//exit;


$GLOBALS['dps'] = 0;
$GLOBALS['start_timer'] = 0;

require_once('shmop3.class.php');

$base = event_base_new();

$event_timer = event_timer_new();
event_timer_set($event_timer, 'handler_timer', $event_timer);
event_base_set($event_timer, $base);
event_timer_add($event_timer, 1000000);
event_add($event_timer);

SHMOP::init('client', 'proc_server.php', 200000, $base);

$GLOBALS['file'] = fopen('out.avi', 'wb');

event_base_loop($base);

function IO_signal()
{
if(!SHMOP::feof())
{
$data = SHMOP::read();

$GLOBALS['dps'] += strlen($data);

if($data !== '')
{
fwrite($GLOBALS['file'], $data);
SHMOP::send_signal();
}
}

else
{
fclose($GLOBALS['file']);
echo "close file\n";
}
}


function handler_timer($res, $status, $timer)
{
if($GLOBALS['start_timer'] == 0)
{
$GLOBALS['start_timer'] = 1;
// Start file
SHMOP::send_signal();
}
echo 'Data Per Sec: '.convert($GLOBALS['dps'])."\n";
$GLOBALS['dps'] = 0;
event_timer_add($timer, 1000000);
}

function convert($size)
{
$unit=array('b','kb','mb','gb','tb','pb');
return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
}

?>


<?php

set_error_handler('my_error_handler');

function my_error_handler($no,$str,$file,$line)
{
file_put_contents('./err.log', $no.$str.$file.$line."\n", FILE_APPEND);
}

require_once('shmop3.class.php');

$GLOBALS['file'] = fopen('in.avi', 'rb');

$base = event_base_new();
SHMOP::init('server', 'proc_server.php', null, $base);
event_base_loop($base);

function IO_signal($fd, $events, $arg)
{
if(!feof($GLOBALS['file']))
{
if(SHMOP::canwrite())
{
SHMOP::write(fread($GLOBALS['file'], 165536));
SHMOP::send_signal();
}
}

else
{
if(SHMOP::sendeof())
SHMOP::send_signal();
fclose($GLOBALS['file']);
}
}


?>


Скорость порядка 2 гигабайт в сек. Если конечно юзать не файл, а из памяти писать.

Смысл в том, что reader может заниматься своими делами, а witer может читать данные в это время, как только блок был прочитан от отправляет сигнал о готовности и у ридера срабатывает евент на чтение и блок копируется уже не с диска, а из сегмента разделяемой памяти, что уменьшает латентность при чтении и не вызывает ступоров.

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

Спустя 49 минут, 13 секунд (23.04.2012 - 13:15) Kolgarn написал(а):
Нет. Не cs.
К сожалению я не имею никакой возможности модифицировать программу, которая пишет.
И с блокировкой вы правы. Постоянно при большом потоке данных попросту теряю часть строк.

Так же по ТЗ висит ограничение на создание демона.
Сами по себе ошибки не страшны генерирующиеся, а вот потеря данных очень критична.

К сожалению в этом плане я никак не могу изменить процесс выдачи. Моя часть в этом только чтение онного.

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

Спустя 9 минут, 1 секунда (23.04.2012 - 13:24) I++ написал(а):
Копирование файла не выход. Могу предложить костыль достаточно эффективный.

Создать виртуальный жесткий диск размером 1 гиг в ОЗУ и пользоваться fopen и блокировкой. Но не ясно, программа пишущая в файл проверяет блокировку или нет, учли ли программеры это.

Спустя 13 минут, 28 секунд (23.04.2012 - 13:37) Kolgarn написал(а):
Цитата (I++ @ 23.04.2012 - 11:24)
Копирование файла не выход. Могу предложить костыль достаточно эффективный.

Создать виртуальный жесткий диск размером 1 гиг в ОЗУ и пользоваться fopen и блокировкой. Но не ясно, программа пишущая в файл проверяет блокировку или нет, учли ли программеры это.

А можно подробнее по этому варианту? Не сталкивался ранее.

Спустя 1 минута, 46 секунд (23.04.2012 - 13:39) ADiel написал(а):
мне кажется, если к этому файлу привязать libevent, то постоянно запущенный демон будет реагировать на запись вызовом функции.

Спустя 25 минут, 8 секунд (23.04.2012 - 14:04) I++ написал(а):
Цитата (ADiel @ 23.04.2012 - 15:39)
мне кажется, если к этому файлу привязать libevent, то постоянно запущенный демон будет реагировать на запись вызовом функции.

Ифна проверенна?

Цитата
А можно подробнее по этому варианту? Не сталкивался ранее.

Спустя 12 минут, 6 секунд (23.04.2012 - 14:16) Kolgarn написал(а):
Цитата (I++ @ 23.04.2012 - 12:04)
Цитата (ADiel @ 23.04.2012 - 15:39)
мне кажется, если к этому файлу привязать libevent, то постоянно запущенный демон будет реагировать на запись вызовом функции.

Ифна проверенна?

Цитата
А можно подробнее по этому варианту? Не сталкивался ранее.

извиняюсь, что сразу не указал - все это должно крутиться под виндой sleep.gif

Спустя 6 минут, 4 секунды (23.04.2012 - 14:22) I++ написал(а):

Спустя 7 минут, 24 секунды (23.04.2012 - 14:30) ADiel написал(а):
Цитата (I++ @ 23.04.2012 - 12:04)
Ифна проверенна?

Нет, потому и написал "кажется".
Думаю, сегодня найду время проверить.

Спустя 4 минуты (23.04.2012 - 14:34) I++ написал(а):
Цитата (ADiel @ 23.04.2012 - 16:30)
Цитата (I++ @ 23.04.2012 - 12:04)
Ифна проверенна?

Нет, потому и написал "кажется".
Думаю, сегодня найду время проверить.

На винде скорее всего не будет работать + libevent ручками собирать, благо я мануал уже написал smile.gif

Спустя 4 минуты, 4 секунды (23.04.2012 - 14:38) ADiel написал(а):
А в программе, которая в файл пишет как реализована запись? Если просто перенаправляется поток вывода, можно его направить сразу в php скрипт.

Спустя 5 минут, 32 секунды (23.04.2012 - 14:43) Kolgarn написал(а):
Там такой заказчик, что чую придется мне покататься пока настрою всем.
Да и программа которая пишет не айс. Попробую сегодня с flock'ом. Будем надеяться там есть очередь на запись, может хоть проблему пропуска строк решу.

Да идиотично там реализовано. Тупо в папочкe Log создается файл по активной сессии и все пишется туда.

Спустя 2 часа, 58 минут, 56 секунд (23.04.2012 - 17:42) Kolgarn написал(а):
Такс... как результат проверки:
Прекрасно файл блокируется, вопрос исключительно в том, что в случае невозможности записать он тыкается по несколько раз и в результате пишет одну и туже запись с РАЗНЫМ временем несколько раз подряд.

Спустя 1 день, 18 часов, 15 минут, 3 секунды (25.04.2012 - 11:57) Kolgarn написал(а):
Цитата (ADiel @ 23.04.2012 - 12:30)
Цитата (I++ @ 23.04.2012 - 12:04)
Ифна проверенна?

Нет, потому и написал "кажется".
Думаю, сегодня найду время проверить.

не проверил? А то было бы идеально для работы...
Сам из-за слабого представления не могу быть уверен в верности того, что получилось sleep.gif

Спустя 2 часа, 28 минут, 5 секунд (25.04.2012 - 14:26) I++ написал(а):
Говорю не парься ставь ram диск и в проге укажи логи туда кидать и читай файл без блокировки, только проверяй "\n" символ, если его нет, fseek юзай, чтобы откатиться и спать в ожидании когда лог допишется.

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

Спустя 1 день, 4 часа, 2 минуты, 30 секунд (26.04.2012 - 18:28) Guest написал(а):
Цитата (I++ @ 25.04.2012 - 12:26)
Говорю не парься ставь ram диск и в проге укажи логи туда кидать и читай файл без блокировки, только проверяй "\n" символ, если его нет, fseek юзай, чтобы откатиться и спать в ожидании когда лог допишется.

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

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

Реализовал пока так:
1) Вешаю flock
2) Читаю безостановочно
3) Если нет информации(строка пустая) - снимаю flock и сплю.

Дополнение:
stream_get_line переодически багает и не видит разграничений по строке - т.е. читает сразу все записанные строки.
Справился путем explode по "\r" и trim

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

1) Идет активация действия
2) Она записала активацию
3) Сразу за не деактивацию, но т.к. не удалось записать деактивацию из-за flock'а - пишет опять активацию и деактивацию.

Так же бывает момент, когда деактивацию удалось записать, а активацию нет и пишет без повторной деактивации. При этом в логе используется время ЗАПИСИ, что еще дополнительно усложняет анализ.

Спустя 36 минут, 42 секунды (26.04.2012 - 19:05) I++ написал(а):
Если это так важно, есть возможность связи с разработчиком?

Спустя 12 минут, 39 секунд (26.04.2012 - 19:17) Guest написал(а):
Цитата (I++ @ 26.04.2012 - 17:05)
Если это так важно, есть возможность связи с разработчиком?

Есть. Но там отношение:
"Вы все быдло - мы короли и может быть через пару месяцев если нам понравится и будет ТАКАЯ уж необходимость мы введем то, что нужно"
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.