[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Несколько скриптов в одном php-файле на сервере
mi.rafaylik
Доброе утро, ребята! )
У меня в одном php-файле (скажем "обработчик1") есть несколько скриптов, выполняемых сервером, а именно:
  • запуск цикла проверки "файла1" на наличие новых сообщений
  • запись нового сообщения в файл1
  • периодическая чистка архива ("файл2" к примеру) от старых сообщений
Вопрос: стоит ли вынести, например, "скрипт запись нового сообщения в файл" в отдельный php-файл (например "обработчик2"), и поможет ли это распределить процессы сервера? Или же не имеет значения, сколько скриптов паралельно выполняются в одном файле?
Сформулирую вопрос иначе: если сервер выполняет цикл (процесс проверки новых сообщений) значит файл "обработчик1" занят, или нет?
Помогите плиз понять схему, как всё происходит )
I++
Если файл 1 в который идет и запись и чтение, смысла создавать отдельные процессы на чтение/запись = ноль. По той причине, что файл будет залочен до той поры, пока не освободится от записи. (если используются "локи")

Цитата
запуск цикла проверки "файла1" на наличие новых сообщений


Если есть возможность логику эту нужно переписать на срабатывание события, а не перебор в цикле файла.
Valick
Цитата
Или же не имеет значения, сколько скриптов паралельно выполняются в одном файле?

кто вам сказал что они выполняются параллельно? smile.gif
Цитата
поможет ли это распределить процессы сервера?

вам бы книжек умных почитать smile.gif
___
распределять логику по файлам нужно, хотя бы по причине объема кода занимающего оперативную память, естественно это не означает создание 1000 файлов по одной строчке кода. Должны быть логически завершенные блоки, из которых в последствии формируется приложение (как дом из кирпичиков) путем подключения тех или иных блоков. Более того в этих блоках, должно быть разделение, на еще более мелкие блоки (например функции).

_____________
Стимулятор ~yoomoney - 41001303250491
mi.rafaylik
Да, пожалуй, пойду за учебник, спасибо )
Суть понял.
mi.rafaylik
Цитата
Если есть возможность логику эту нужно переписать на срабатывание события, а не перебор в цикле файла.

Понял смысл, так и сделаю. Руки и голова есть, значит и возможность есть )
Игорь_Vasinsky
Цитата
Сформулирую вопрос иначе: если сервер выполняет цикл (процесс проверки новых сообщений) значит файл "обработчик1" занят, или нет?


сценарий (скрипт, код) будет выполняться последовательно сверху - вниз
с файлами мутить такую тему.... пожалейте себя, используйте БД

_____________
HTML, CSS (Bootstrap), JS(JQuery, ExtJS), PHP, MySQL, MSSql, Posgres, (TSql, BI OLAP, MDX), Mongo, Git, SVN, CodeIgnater, Symfony, Yii 2, JiRA, Redmine, Bitbucket, Composer, Rabbit MQ, Amazon (SQS, S3, Transcribe), Docker
mi.rafaylik
Цитата (Игорь_Vasinsky @ 12.02.2013 - 23:14)
с файлами мутить такую тему.... пожалейте себя, используйте БД

Мне уже не раз говорили, что DB прийдётся использовать, и я это понимаю. Пожалуй пора разобраться как они устроены, правда.
Цитата (I++ @ 12.02.2013 - 09:59)
Если есть возможность логику эту нужно переписать на срабатывание события, а не перебор в цикле файла.

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

Система такая:
Со стороны клиента - при открытии страницы (или блока на странице) загружается содержимое файла archive.txt, после него (ниже) загружается содержимое data.txt. Все новые данные будут добавляться ещё ниже, вот так:
  • содержимое archive.txt
  • содержимое data.txt
  • ... новые данные, которые будут получены, добавляются ниже
  • ... следующие новые данные, и т.д.
Как тоолько данные получены, через ajax отправляется длительный запрос (long-poll) в ожидании новых данных.
На сервере запускается цикл (с интервалом 1 секунда) который проверяет текстовый файл data.txt (или БД, в перспективе буду осваивать) на наличие новых данных (сравнивает его содержимое с предыдущими полученными данными).
В data.txt хранится только одна строка текста (одно сообщение), старые же сообщения перемещаются в архив archive.txt (дозапись в конец архива) перед записью нового сообщения.
Т.е. этот data.txt служит в качестве буфера последнего сообщения.
Почему придумал сделать так, а не иначе? Если хранить все сообщения в data.txt, то (даже если чистить старые сообщения) в запросе прийдётся передавать существенный вес (пусть даже десятки килобайты), а это трафик.
При записи в файлы data.txt или archive.txt использую блокировку записи flock, чтоб предотвратить одновременную запись разными пользователями. Перед снятием блокировки ставлю интервал 1 секунду sleep(1). Объясняю зачем. Если не использовать паузу, то "пользователь 1" записывает данные в data.txt, после этого блокировка снимается и "пользователь 2" пишет в data.txt следующие данные. Но напомню, что цикл проверки новых сообщений - 1 секунда, и если данные в файл записываются чаще, чем 1 секунда, то цикл проверки не успеет "увидеть" сообщения, записанные между этим интервалом. Именно поэтому я решил после записи в data.txt делать паузу 1 секунда, и только после этого снимать блокировку. Если два запроса на запись отправлены почти одновременно, один из запросов блокирует файл и записывает данные, а второй запрос висит в очереди и выполняется после снятия блокировки предыдущим запросом. Теперь вы знаете, как происходит запись.

Но мой попутный вопрос - о чтении. Хочу прояснить, как выполняется скрипт на сервере?
Если я правильно понял - при запросе новых данных, для каждого пользователя серверный скрипт выполняет отдельный цикл проверки сообщений. Подскажите где почитать, чтоб сделать что-то вроде канала подписки, чтоб не нагружать серв?
I++
Libevent + websockets + SHM IPC решит все проблемы и нагрузка в 1000 запросов в секунду будет обрабатываться в лет.

Пишется единственный демон на php

Демон будет выступать в роли фронтенда который будет принимать подключения по websockets, так же он будет открывать разделяемый участок памяти который будет проверять на входящие сообщения и если сообщение есть будет его отсылать определенному юзеру, либо броадкастом всем юзерам. Но тут есть один небольшой геморой, SHM IPC не поддерживает полинг, а значит, придется гонять цикл проверки на новые мессаги, что не есть гуд, но я придумал костыль с SIGIO это не быстрее чем гонять цикл, зато уменьшит задержки (до определенных моментов), pcntl функции передачи сигналов конечно быстрая штука, но если такие сигналы будут идти по 10к в секунду, тут будет узкое место, но в таком случае PHP вообще не уместен.

А теперь о том, как юзера будут отправлять мессаджи, логику работы приложения я не знаю, возможно это будет отдельный скрипт который будет проверять правильность введенных данных и при успехе записывать мессадж в разделяемый участок памяти, но я бы не рекомендовал этот метод, лучше перевести всю логику на обвязку libevent + websockets без участия разделяемой памяти. Если демон будет выполнять простые операции в виде отправки данных всем юзерам (как в чатах), то отдельный "тяжелый" процесс нам не понадобится, который может отрабатывать столько, сколько хочет, а потом возвращать результат обратно.

Например: пользователь отправил запрос на обработку каких либо данных, при этом на их обработку понадобится потратить 100 секунд, в этом случае демон зависнет в обработке этих данных и перестанет принимать данные от других пользователей, так дело не пойдет и такие тяжелые процессы лучше разделить на выполнение в другом процессе, вот тут может придти на помощь разделяемая память. Я по этому поводу гемороился в свое время, но в конечном итоге меня не устроила скорость обработки огромных массивов данных, при скорости ОЗУ в 20 гб/с скорость падала до 6 гб/с для соединения типа точка-точка.

http://phpforum.ru/index.php?showtopic=59928
http://phpforum.ru/index.php?showtopic=59689

Вот тут и возникло то узкое место с сигналами в pcntl.

Если массивы данных будут не такие большие, то вариант очень не плохой, но инфы в интернете об этом нет и не будет, извращенцев и адептов PHP-BDSM практически нет smile.gif

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

Рекомендую глянуть http://phpdaemon.net/ готовое решение, есть даже примеры работы с вебсокетами http://habrahabr.ru/post/79377/

Помню давно еще чатик не выдержал хабраэффекта biggrin.gif Потому что Хабровчане народ лютый, начал DDoSить сокеты и чат успешно падал, автор не реализовал проверку на флуд сокета, а делается он достаточно просто с замером DPS (Data per second) + dpIPs (data per ip second)
mi.rafaylik
Проверка на флуд у меня в клиентской части, это важный момент, да )) Ещё не читал, как лучше она реализовывается, но сначала решил придумать свой вариант на jQuery - после нажатия Enter отправляю сообщение и назначаю return false на событие нажатия Enter для данного текстового поля, а после таймаута 1 секунда разрешаю нажатие Enter.
// задаю переменную для реализации защиты от флуда
var chatSendTimeout = 0;
$('.write-post').keydown(function(e) {
if (e.which == 13) {
// если нажата Shift - перевод строки
if (e.shiftKey) {
// если enter нажат раньше чем через секунду после отправки
} else if (chatSendTimeout == 1) {
return false;
// нажат enter - отправка сообщения
} else {
chatSend(); // внешняя функция отправки запроса
$('.write-post').val(''); // очищаю текстовое поле
chatSendTimeout = 1; // назначаю кодовое значение переменной
setTimeout(function(){
chatSendTimeout = 0; // очищаю переменную через секунду
}, 1000);
return false; // запрет перевода строки после отправки
}
}
}
);

А вот про переполнение выделяемой памяти и не подумал..
Спасибо большое за материал, буду разбирать по полочкам
I++
Цитата
Проверка на флуд у меня в клиентской части


Это полностью не решает проблему тут нужны проверки как у клиента так и на сервере. Проверки на клиенте это отлично, отсеет массу идиотов, но есть и более умные представители сети, которых такие проверки не остановят.

-----
Как я вижу, тут чат, отлично значит phpdaemon отлично подойдет. Там нагрузок практически нет с вычислениями, просто слать туда сюда запросы и даже база данных не нужна. Главное реализовать массив в виде стека для сохранения например последних 10 сообщений и если человек только зашел в чат ему показывается последние 10 сообщений.

1 демон может запросто держать 1000 посетителей, а websocket обеспечит меньшее потребление трафика. Так же если по хитрому модифицировать код, можно сделать множество чатов всего с 1 демоном.
mi.rafaylik
Цитата (I++ @ 13.02.2013 - 19:23)
значит phpdaemon отлично подойдет.

Видимо то чем я и интересовался, буду изучать )
Цитата (I++ @ 13.02.2013 - 19:23)
Главное реализовать массив в виде стека для сохранения например последних 10 сообщений

Такой массив у меня реализован. Таким образом:
1. Пользователь зашёл в чат, получает загруженные последние данные из файлов archive.txt и data.txt.
2. Через пару секунд отправляется отдельный запрос в бэкэнд, который проверяет условие - если в файле archive.txt количество строк больше например десяти, то первые строки удаляются. Такая себе периодичесткая чистка архива. Опять же сделал это по своим представлениям, а не так "как нужно".
Подробней на php выглядит вот так:
// путь к файлу архива
$fileArchive = 'chat.archive.php';
// получен запрос удаление старых сообщений в архиве при входе пользователя
if (isset($_POST['chatEnter'])) {
// получаю массив архива
$chatArchive = file($fileArchive);
// получаю количество строк
$chatCountLines = count($chatArchive);
// устанавливаю лимит допустимого количества строк (сообщений)
$chatLimitLines = 10;
// если лимит превышен - выполняется действие
if ($chatCountLines > $chatLimitLines) {
// вычисляю количество строк, которые нужно удалить
$chatRemoveLines = $chatCountLines - $chatLimitLines;
// задаю, начиная с какой и по которую строку нужно выполнить удаление - с нулевой по (полученное значение)
$chatRemoveArray = array_splice($chatArchive, 0, $chatRemoveLines);
// собственно удаление строк (старых сообщений)
unset($chatArchive['$chatRemoveArray']);
// запись почищенного архива обратно в файл
file_put_contents($fileArchive, $chatArchive);
}
}

Единственное я наверное не очистил память после этого действия. Кажется это flush() ? Или просто die() ?
Быстрый ответ:

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