Я хочу написать работника для beanstalkd в php, используя контроллер Zend Framework 2. Он запускается через CLI и будет работать вечно, запрашивая задания из 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 будет обрабатывать это в долгосрочной перспективе и должен ли я принять какие-либо особые меры предосторожности, чтобы это не блокировалось?
Это я действительно рассматривал и мог быть полезным (но, пожалуйста, исправьте, если я ошибаюсь и добавлю больше, если это возможно):
- Используйте gc_enable() перед запуском цикла
- Используйте gc_collect_cycles() на каждой итерации
- Отменить
$job
на каждой итерации - Явно отключить ссылки в
__destruct()
от$job
(NB: обновите здесь)
Я провел несколько тестов с произвольными заданиями. Работы, которые я включил, были: "простые", просто установили значение; "longarray", создайте массив из 1000 значений; "продюсер", пусть цикл введет $pheanstalk
и добавит три очереди в очередь (так что теперь есть ссылка с задания на beanstalk); "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 не увеличено и сведено к минимуму, но со временем увеличивается.