Comments 59
В своем jabber боте я реализовал немного по иному, парситься сообщение по типу:
<имя файла> параметры
и постоянно подключаю файл который формирует переменную, ее и отдаю на вверх.
Т.е если когда приходит сообщение:
формируется код:
Заменять фукнции так нельзя, но проблему «на лету» решает.
<имя файла> параметры
и постоянно подключаю файл который формирует переменную, ее и отдаю на вверх.
Т.е если когда приходит сообщение:
md5 123
формируется код:
$param = 123;
$result = '';
include("md5.php");
$jabber->send($result);
Заменять фукнции так нельзя, но проблему «на лету» решает.
Кешеры опкодов не одобряют этот пост
на самом деле проблем с кешерами не особо есть, по крайней мере пример прекрасно работает с АРС. если файл изменяется, кешер его все равно загрузит снова
PHP такой PHP
xamelion такой xamelion
И всё-таки… Зачем извращаться на PHP? Есть куча языков, нормально поддерживающих горячую замену кода — даже перекомпилирующих код в реальноом времени, чтобы быстрее выполнялся.
можно было реализовать одного демона, который управляет другими: поднимает, загружает, отдаёт задания, тогда не проблема отловив обновления форкнуть новую версию. заодно решится проблема с течкой памяти.
такой демон гораздо сложнее + надо хранить текущее состояние всех объектов и переменных — то есть, изначально проектировать под такое поведение весь код. А как быть с ресурсами? Их не получится переносить между процессами, что в ряде случаев недопустимо.
демон — очень прост — бесконечный цикл в нём проверка новой версии, если она есть — запуск процесса опционально послать сигнал на завершение старому. получается прекрасно масштабируемое приложение. общие ресурсы можно хранить в мемкэше или шаред мемори. можно даже легко запускать сотни воркеров на разных серваках.
Я думаю, Вам будет интересно почитать и подумать над над Inversion of Control и Strategy software patterns.
И вообще:
как-то проще для понимания…
И вообще:
-- main.php -- interface IWorker { function doWork(); } class Worker1 extends IWorker { function doWork() { ... } } $worker = new Worker1(); function main() { ... if ($newClassAvailable) require($newClass); ... $GLOBALS['worker']->doWork(); ... } newclass.php class Worker2 extends IWorker { function doWork() { ... } } $GLOBALS['worker'] = new Worker2();
как-то проще для понимания…
extends? Или все же implements?
Паттерны это хорошо, но нужно далеко не везде. :) Вместо глобалс подумайте над Registry :) если уже зашли в такие дебри как IoC
да и код не претендует на промышленность — просто исследование самой возможности.
runkit умеет переопределять классы, методы, функции. Оно подойдет ;-)
да, однако специфичный модуль и мало распространён. Но там есть возможности переопределения и переименовывания функций — остается только навесить код для извлечения и подмены на лету, однако мне кажется, что с ним будет только сложнее. Да и runkit, как по мне, серьезный хак на уровне самого процессора языка.
что насчет автоматического выгружения старого кода?
В общем, я бы использовал Unix-сигналы, а не проверки файла, если такое возможно.
+1, думаю топикстартеру кудато сюда: ru2.php.net/manual/en/ref.pcntl.php
многое конечно зависит от требований и кода который унаследован от предыдущих разработчиков, но всетаки уже есть рабочий механизм, который обычно рекомендуют использовать
многое конечно зависит от требований и кода который унаследован от предыдущих разработчиков, но всетаки уже есть рабочий механизм, который обычно рекомендуют использовать
как именно сигналы могут помочь в указанной теме?
Наверное, лучше всё же заменять объекты, а не функции. Тогда просто делаем singleton factory-класс, который по вызову возвращает объект текущей версии подключаемого класса. Подключаемые версии одного класса должны реализовать один интерфейс, это позволит использовать проверку instanceof и увеличит надежность. Поключаемый класс должен уведомить этот синглтон о своей новой версии.
А с кэшерами, думаю, особой проблемы быть не должно. Можно автоматом при обновлении класса на сайте ложить его в новый файл с именем по версии класса. Всё равно кэшер для этих классов, которые сидят в памяти, преимуществ не даст, а будет работать только для клиентских скриптов.
А с кэшерами, думаю, особой проблемы быть не должно. Можно автоматом при обновлении класса на сайте ложить его в новый файл с именем по версии класса. Всё равно кэшер для этих классов, которые сидят в памяти, преимуществ не даст, а будет работать только для клиентских скриптов.
можно. Однако не всегда стоит городить объекты и интерфейсы. Уведомить он может, в принципе, скорее всего также только таким способом — возвратом некоторой информации в include/require. Как еще варианты — проверка через список определенных классов после инклуда, но требует большей работы и меньшая надежность.
Почему только возвратом?
Например, у нас есть класс MyTester, реализующий интерфейс MyTester_Interface.
В свежезагруженном файле лежит класс:
Соответственно, клиент (т.е. вызывающий скрипт), когда ему нужен объект MyTester текущей версии, вызывает:
$tester=TesterRegistry::factory(); $tester->test().
TesterRegistry хранит имя текущей версии класса MyTester, по запросу factory() инстанцирует и возвращает его.
Такой подход позволяет с помощью интерфейса MyTester_Interface проверять, что мы всё правильно написали в классе (да, я КО), и что передали в нужный регистр объект нужного класса (в регистре нужна проверка $object instanceof MyTester_Interface. К несчастью, судя по всему, её можно провести только во время инстанцирования, но на этот случай регистр может хранить и имя предыдущей версии).
Кроме того, можно подменять разные варианты tester'а при написании кода или выполнении путем передачи разных классов TesterRegistry клиенту в виде переменной. Был MyTester — стал TheirTester.
А также, имхо, улучшатся переспективы автоматических юнит-тестов.
Я бы ещё добавил, что при сборке/обновлении сайта отдельные версии класса можно ложить в файлы вида MyTester_V99.php, а в главном MyTester.php инклюдить последнюю версию класса. Таким образом, при старте сайта сразу подключатся и зарегистрируются текущие версии. Хотя тут ещё можно подумать насчет отката к предыдущей версии, если подключение класса вызывает ошибку или интерфейс не тот, но эти вопросы, наверное, лучше решать на отладке до выкладки на сайт.
Например, у нас есть класс MyTester, реализующий интерфейс MyTester_Interface.
В свежезагруженном файле лежит класс:
class MyTester_V99 implements MyTester_Interface { public function test() { ... } } if (class_exists('TesterRegistry')) { TesterRegistry::register('MyTester_V99'); }
Соответственно, клиент (т.е. вызывающий скрипт), когда ему нужен объект MyTester текущей версии, вызывает:
$tester=TesterRegistry::factory(); $tester->test().
TesterRegistry хранит имя текущей версии класса MyTester, по запросу factory() инстанцирует и возвращает его.
Такой подход позволяет с помощью интерфейса MyTester_Interface проверять, что мы всё правильно написали в классе (да, я КО), и что передали в нужный регистр объект нужного класса (в регистре нужна проверка $object instanceof MyTester_Interface. К несчастью, судя по всему, её можно провести только во время инстанцирования, но на этот случай регистр может хранить и имя предыдущей версии).
Кроме того, можно подменять разные варианты tester'а при написании кода или выполнении путем передачи разных классов TesterRegistry клиенту в виде переменной. Был MyTester — стал TheirTester.
А также, имхо, улучшатся переспективы автоматических юнит-тестов.
Я бы ещё добавил, что при сборке/обновлении сайта отдельные версии класса можно ложить в файлы вида MyTester_V99.php, а в главном MyTester.php инклюдить последнюю версию класса. Таким образом, при старте сайта сразу подключатся и зарегистрируются текущие версии. Хотя тут ещё можно подумать насчет отката к предыдущей версии, если подключение класса вызывает ошибку или интерфейс не тот, но эти вопросы, наверное, лучше решать на отладке до выкладки на сайт.
Более того, можно не использовать класс регистратора а-ля глобальную переменную. Вместо этого можно перед инклюдом объявить переменную регистратора как локальную в функции:
Тогда можно в подключаемом классе сделать:
function includeTester($registry) { include('MyTester_V99.php'); }
Тогда можно в подключаемом классе сделать:
Ну и костыли вы предлагаете :/
> а можно ли не останавливая скрипт, подменить функцию, которая выполняется?
Учи паттерны дружок)))
> а можно ли не останавливая скрипт, подменить функцию, которая выполняется?
Учи паттерны дружок)))
серебренная пуля небось? Какой именно паттерн это реализует? в РНР? С удовольствием почитаю.
Factory Method будет возвращать тебе нужный объект (нужной версии). только у тебя система основана на версиях функций. а желательно на версиях объектов базироваться.
Паттерны языковонезависимые.
Ну в теории, при достаточно долгой работе такого демона и интенсивной его доработке «на лету» означенным способом может всплыть негативный эффект от забитого пространства имен функций. Хотя это маловероятно пожалуй)
Спасибо, интересно!
А не проще ли будет хранить в некоторой переменной/свойстве лямбда-функцию? Тогда демону будет пофиг что вызывать: имя-то не меняется :)
UFO just landed and posted this here
При возникновении ошибок на демоне нужно архитектуру строить так, чтобы ошибки не вызывали фатального падения всей системы и подключенных пользователей.
Упал демон -> поднялся -> продолжил работать с того места на котором остановился.
А PHP это или нет, не так уж и важно. У меня в одной он-лайн игре PHP демоны работают без странностей и неприятностей.
Упал демон -> поднялся -> продолжил работать с того места на котором остановился.
А PHP это или нет, не так уж и важно. У меня в одной он-лайн игре PHP демоны работают без странностей и неприятностей.
UFO just landed and posted this here
Никогда проблем с памятью не было.
Сейчас проверил процесс, отвечающий за 2 000 ботов — крутится уже с месяц после последнего перезапуска. Памяти полпроцента. Никакой тенденции к разрастанию.
Все завязано на цикл. Все объекты создаются внутри метода, и, мне кажется, garbage collector ПХП 5.2.9 должен прекрасно с этим справляться.
Сейчас проверил процесс, отвечающий за 2 000 ботов — крутится уже с месяц после последнего перезапуска. Памяти полпроцента. Никакой тенденции к разрастанию.
Все завязано на цикл. Все объекты создаются внутри метода, и, мне кажется, garbage collector ПХП 5.2.9 должен прекрасно с этим справляться.
> Упал демон -> поднялся -> продолжил работать с того места на котором остановился.
Как вы сохраняете состояние задачи/объекта, чтобы продолжить с того же места?
Как вы сохраняете состояние задачи/объекта, чтобы продолжить с того же места?
>>
Понятно, что текущая версия кода и имя текущей функции вряд ли будет пустое значение, либо fakse, либо 0. НО! Все же если сравниваете с null'ом, то сравниваете именно с ним! ===
if (($this->_current_fn != null) && ($this->_current_timestamp != null))
Понятно, что текущая версия кода и имя текущей функции вряд ли будет пустое значение, либо fakse, либо 0. НО! Все же если сравниваете с null'ом, то сравниваете именно с ним! ===
UFO just landed and posted this here
Существует ещё функция override_function и runkit_function_redefine, но обе требуют PECL или расширения
В задаче, в которой кусок кода может меняться мне было проще постоянно делать require нужного куска кода. При изменениях сразу подхватывается обновленный файл. Минус — затраты на постоянные вызовы require. Можно улучшить, например проверяя время изменения файла с куском кода.
если не морочиться с runkit/classkit или принудительной перекомпиляцией байткода через apc (есть и такая возможность, см apc_compile_file) — наболее простое и быстрое решение задачи — все функции держать простой «картой» замыканий и через сигнал перегружать карту (ну и вариации на тему), а не проверять каждый раз файл.
о каком сигнале идет речь и кто его должен посылать?
например, SIGUSR2. посылать сигнал — очевидно тот, кто обновляет код.
если так — не было бы смысла все это делать. Задача стоит — просто обнови файл — и скрипт подхватывает изменения автоматически. с минимальным изменением самого скрипта. если задаться целью строить уж такую систему, тогда там много чего можно нагородить. а в данном случае все укладывается в пару строк и пару вызовов.
Sign up to leave a comment.
Горячая замена кода (code hot swapping) в РНР