Особенности памяти для длительных скриптов php - PullRequest
14 голосов
/ 02 апреля 2012

Я хочу написать рабочий для beanstalkd в php, используя контроллер Zend Framework 2. Он запускается через интерфейс командной строки и будет работать вечно, запрашивая задания у beanstalkd, например , этот пример .

В простом псевдоподобном коде:

while (true) {
    $data   = $beanstalk->reserve();

    $class  = $data->class;
    $params = $data->params;

    $job    = new $class($params);
    $job();
}

$job имеет здесь __invoke() метод, конечно. Однако некоторые вещи в этих заданиях могут выполняться долго. Некоторые могут работать со значительным объемом памяти. Некоторые могли внедрить объект $beanstalk, чтобы самим начинать новые задания, или иметь экземпляр Zend\Di\Locator для извлечения объектов из DIC.

Меня беспокоит эта настройка для производственных сред в долгосрочной перспективе, поскольку, возможно, могут возникать циклические ссылки, и (на данный момент) я явно не "делаю" какую-либо сборку мусора, в то время как это действие может выполняться неделями / месяцами / годами. *.

*) В beanstalk reserve является блокирующим вызовом, и если работа недоступна, этот работник будет ждать, пока не получит ответ от beanstalk.

Мой вопрос : как php справится с этим в долгосрочной перспективе, и я должен принять какие-то особые меры предосторожности, чтобы это не блокировалось?

Это я рассмотрел и мог бы быть полезным (но, пожалуйста, исправьте, если я ошибаюсь, и добавьте больше, если это возможно):

  1. Используйте gc_enable () перед началом цикла
  2. Используйте gc_collect_cycles () в каждой итерации
  3. Unset $job в каждой итерации
  4. Явно сброшенные ссылки в __destruct() из $job

(примечание: обновление здесь)

Я провел несколько тестов с произвольными заданиями. Задания, которые я включил, были: «простые», просто установите значение; "longarray", создать массив из 1000 значений; «продюсер», пусть цикл внедрит $pheanstalk и добавит три простых задания в очередь (так что теперь есть ссылка от задания на бобовый стебель); «locatoraware», где задано Zend\Di\Locator и созданы все типы заданий (хотя и не вызваны). Я добавил в очередь 10 000 заданий, а затем зарезервировал все задания в очереди.

Результаты для "simplejob" (потребление памяти на 1000 заданий, с memory_get_usage())

0:     56392
1000:  548832
2000:  1074464
3000:  1538656
4000:  2125728
5000:  2598112
6000:  3054112
7000:  3510112
8000:  4228256
9000:  4717024
10000: 5173024

Выбор случайной работы, измерения, как указано выше. Распределение:

["Producer"] => int(2431)
["LongArray"] => int(2588)
["LocatorAware"] => int(2526)
["Simple"] => int(2456)

Память:

0:     66164
1000:  810056
2000:  1569452
3000:  2258036
4000:  3083032
5000:  3791256
6000:  4480028
7000:  5163884
8000:  6107812
9000:  6824320
10000: 7518020

Код выполнения сверху обновляется до этого:

$baseMemory = memory_get_usage();
gc_enable();

for ( $i = 0; $i <= 10000; $i++ ) {
    $data = $bheanstalk->reserve();

    $class = $data->class;
    $params = $data->params;

    $job = new $class($params);
    $job();

    $job = null;
    unset($job);

    if ( $i % 1000 === 0 ) {
        gc_collect_cycles();
        echo sprintf( '%8d: ', $i ), memory_get_usage() - $baseMemory, "<br>";
    }
}

Как все замечают, потребление памяти в php не используется и поддерживается на минимуме, но увеличивается со временем.

Ответы [ 3 ]

2 голосов
/ 02 апреля 2012

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

Мой пост в http://www.phpscaling.com/2009/06/23/doing-the-work-elsewhere-sidebar-running-the-worker/ содержит несколько примеров сценариев оболочки для повторного запуска сценариев.

2 голосов
/ 08 апреля 2012

Я закончил тестирование моей текущей строки кода для строки, после чего я пришел к следующему:

$job = $this->getLocator()->get($data->name, $params);

Используется инъекция зависимостей Zend\Di, в которой менеджер экземпляров отслеживает экземпляры в течение всего процесса.Поэтому после того, как задание было вызвано и могло быть удалено, менеджер экземпляров все еще оставил его в памяти.Неиспользование Zend\Di для создания экземпляров заданий немедленно привело к статическому потреблению памяти вместо линейного.

1 голос
/ 02 апреля 2012

В целях безопасности памяти не используйте циклы после каждого задания последовательности в PHP.Но просто создайте простой bash-скрипт для создания цикла:

while [ true ] ; do
    php  do_jobs.php 
done

Привет, do_jobs.php содержит что-то вроде:

// ...

$data   = $beanstalk->reserve();

$class  = $data->class;
$params = $data->params;

$job    = new $class($params);
$job();


// ...

просто так?;)

...