Это правильный способ использования очереди сообщений?

Я новичок в очередях обмена сообщениями, и сейчас я использую ZeroMQ на моем Linux-сервере. Я использую PHP для записи как клиента, так и сервера. Это в основном используется для обработки push-уведомлений.

Я использую базовый REQ - REP Формально-коммуникационный шаблон для отдельных экземпляров ZMQContext с интерфейсом ввода/вывода, как они продемонстрировали.

Вот минимальный zeromqServer.php код:

include("someFile.php");

$context = new ZMQContext(1);

//  Socket to talk to clients
$responder = new ZMQSocket($context, ZMQ::SOCKET_REP);
$responder->bind("tcp://*:5555");

while (true) {
    $request = $responder->recv();
    printf ("Received request: [%s]\n", $request);

    //  -----------------------------------------------------------------
    //                                    Process push notifications here
    //
    sleep (1); 

    //  -----------------------------------------------------------------
    //                                          Send reply back to client
    $responder->send("Basic Reply");
}

И вот минимизированный ZeroMQ клиент:

$context = new ZMQContext();

//  Socket to talk to server
echo "Connecting to hello world server…\n";
$requester = new ZMQSocket($context, ZMQ::SOCKET_REQ);
$check = $requester->connect("tcp://localhost:5555");

var_dump($check);
$requester->send("json string payload with data required to process push notifications.");

//$reply = $requester->recv();

Итак, что я делаю? Я запускаю zeromqServer.php в качестве фоновой службы, используя команду linux

nohup php zeromqServer.php &

Это выполняется как фоновый процесс. Теперь, когда клиент называет его, он выполняет требуемую работу.

Но проблема в том, что мне нужно перезапустить процесс каждый раз, когда есть изменения в любом из файлов (включая те include -ed в файле zeromqServer).

И более того, как-то через 2-3 дня он просто перестает работать. Процесс не останавливается, но он просто перестает работать.

Мне кажется, что это должна быть проблема сокета, возможно, сокет больше не открыт. В это время мне нужно перезапустить процесс zeromqServer.php.

Q1: В чем может быть проблема?

Q2: И каков правильный способ сделать это?

Ответы

Ответ 1

Это неправильный способ использования очереди сообщений.

A1: Проблема заключается в том, что ваш сервер, наконец, должен заблокировать, так как клиентский код не получает ответа, а для этого требуется REQ/REP -pattern. zeromqServer.php на стороне REP просто не будет пытаться recv() получить другое сообщение от связанного клиента (на стороне REQ модуля формальной связи), пока клиент не будет физически доставлен (во внутренний буфер) и имеет recv() - значение "ответа" со стороны zeromqServer.php.

Более ранние версии ZeroMQ, вер. 2.1 и др., Использовались для неограниченного, бесконечного размера ограничения по умолчанию для внутренних очередей сообщений node и управления памятью, используемых для буферизации низкоуровневых потоков ввода-вывода, до того, как данные были скопированы в O/S -kernel и освобождены от занимаемой памяти ZeroMQ.

Новые версии ZeroMQ, ver 3.x +, имеют так называемые HWM -s (aka High-Water-Mark-s) по умолчанию "всего лишь 1000 сообщений, выделяющих" короткие ", после чего соответствующая часть такого ресурса ZeroMQ начинает блокировать или отбрасывать сообщения.

В то время как реактивная попытка явно увеличить управление HWM-настройками выглядит грязным способом решения основной ошибки дизайна, другое предупреждение ZeroMQ справедливо для этого, чтобы еще раз предостеречь пойти именно в этом направлении (ØMQ не гарантирует, что сокет будет принимать сообщения ZMQ_SNDHWM, а фактический предел может быть целых 60-70% ниже в зависимости от потока сообщений в сокете).

Забыть или не сделать этого ( Ссылка:, поскольку код OP уже продемонстрировал):

//$reply = $requester->recv();

означает, что ваш REQ/REP Формальный шаблон связи "маятник" становится необратимо запертым (навсегда).


A2: Основной REQ/REP Формальный коммуникационный паттерн звучит прямо, но имеет несколько опасных функций, наблюдаемая блокировка - всего лишь одно из них. Некоторые дополнительные шаги могут быть предприняты по коду, для развертывания XREQ/XREP, DEALER/ROUTER и других инструментов, но дизайн следует переупаковать, а не только SLOC на SLOC, поскольку, похоже, много что нужно реализовать, прежде чем разрабатывать код. Одна из главных ошибок заключается в том, что сообщение отправляется после того, как был заказан метод send(). Не в ZeroMQ.

Также код-код должен воспринимать неопределенный характер сообщения, доставляющего и обрабатывающего должным образом как недостающую проблему сообщения, так и любой инцидент блокировки с распределенным сервисом (тупиковая ситуация, ожидание, переполнение буфера-порога, старый/новый-API конфликты, так как нет никакой явной гарантии ни один из ваших одноранговых узлов обмена сообщениями (в ZeroMQ отсутствует центральный брокер сообщений) имеет ту же самую версию API/протокола ZeroMQ, реализованную на нем, на стороне localhost, поэтому действительно есть много новых точек во время разработки кода)


Лучший способ сделать это

Если вы можете доверять и верить в практический опыт, лучшим вашим следующим шагом должно быть скачать и прочитать сказочный Pieter HINTJENS ' книга "Код подключен, том 1" , где у Pieter есть много информации о распределенной обработке, включая множество подсказок и направлений для надежных шаблонов, которые вы хотите реализовать.

Прочитайте книгу, это тоже стоит вашего времени, и вы, вероятно, будете многократно пересматривать книгу, если вы останетесь в распределенном программном обеспечении, поэтому не стесняйтесь начинать прямо сейчас, перепрыгивая на такую ​​кулинарную книгу на 400+ страниц из Мастер Мастеров, Питер ХИНЦЕНС из вопросов.

Почему? Реальный мир обычно намного сложнее

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

Transport Plane + SIG Plane

Чтобы иметь какую-то идею, загляните в следующий пример кода из простого распределенного обмена сообщениями, где aMiniRESPONDER обрабатывает несколько каналов ZeroMQ.

введите описание изображения здесь


Как улучшить вашу реализацию в довольно большом веб-домене PHP-приложения?

Узнайте, как предотвратить конфликты (дизайн-мудрый) и дескриптор (deux-ex-machina) другие конфликты.

У PHP есть собственные правильные синтаксические конструкторы для этого типа алгоритмизации, но архитектура и дизайн в ваших руках, от начала до конца.

Чтобы понять, насколько большен стиль { try:, except:, finally: }, ориентированный на столкновение, с установкой/инфраструктурой системы ZeroMQ/компонентом системы /ZeroMQ, проверьте номера [SoW] только по номерам строк:

14544 - 14800 // a safe infrastructure setup on aMiniRESPONDER side   ~ 256 SLOCs
15294 - 15405 // a safe infrastructure graceful termination          ~ 110 SLOCs

по сравнению с основной логикой сегмента обработки событий aMiniRESPONDER example

14802 - 15293 // aMiniRESPONDER logic, incl. EXC-HANDLERs             ~ 491 SLOCs

Окончательная заметка о распределенных системах на основе ZeroMQ

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

Ответ 2

Я могу лишь частично ответить на этот вопрос. Я понятия не имею, почему процесс зависает через 2-3 дня.

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

somefile.php

$context = new ZMQContext(1);

//  Socket to talk to clients
$responder = new ZMQSocket($context, ZMQ::SOCKET_REP);
$responder->bind("tcp://*:5555");

while (true) {
    $request = $responder->recv();
    printf ("Received request: [%s]\n", $request);

    $subscriptParams = [
        "Request" => $request //Add more parameters here as needed
    ];

    $result = shell_exec("php work.php ".base64_encode(serialize($subscriptParams)));       

    //  Send reply back to client
    $responder->send("Basic Reply");
}

work.php

if (!isset($argv[0])) {
   die("Need an argument");
}

$params = unserialize(base64_decode($argv[0]));
//Validate parameters
$request = $params["Request"];
//  Do some 'work'
//Process push notifications here!
sleep (1); 

Здесь мои предположения:

  • Часть настройки script, например. установка $context и $responder будет оставаться постоянной навсегда (или вы могли бы жить со временем простоя, необходимым для перезапуска script из-за изменений в этом).

  • Начало и конец цикла остаются постоянными, я полагаю, что shell_exec вернет ответ, который может быть использован ответчиком в качестве фактического ответа.

Дополнительные пояснения:

Я использую serialize для передачи массива аргументов, которые нужно использовать work.php. Я делаю декодирование base64, потому что я хочу, чтобы весь аргумент поместился в $argv[0] и не получил разделение на потенциальные пространства, найденные в аргументах.

То же сочетание serialize -> base64_encode и base64_decode -> deserialize может использоваться для результата work.php.

Обратите внимание, что я лично не пробовал этот код, поэтому я не могу гарантировать, что он работает. Я просто не вижу причин, по которым он не должен работать (убедитесь, что php находится в вашем пути или вызовите /usr/bin/php в shell_exec, если это не так).

Стоит отметить, что это решение будет довольно медленным, чем наличие всего кода в одном файле, но это стоимость обновления script на каждой итерации.

Ответ 3

Q2: И каков правильный способ сделать это?

Правильный способ сделать все, на мой взгляд, - выбрать лучший вариант, доступный для вас в начале, чтобы не вкладывать время в метод, который даст вам результаты второго сорта. У меня нет ничего против ZeroMQ, хотя я следую логике, что программисты должны всегда стремиться делать самый чистый код и использовать самые лучшие инструменты. В случае создания очереди сообщений с PHP у вас будет намного больше успеха с Pheanstalk: https://github.com/pda/pheanstalk

Это рекомендуемый вариант с открытым исходным кодом онлайн и отлично работает на Linux. Установка очереди очень проста, и я написал полный ответ о том, как установить pheanstalk в следующем разделе: Невозможно заставить Beanstalkd Queue работать для PHP

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

Производитель сообщений:

<?php
$pheanstalk = new Pheanstalk('127.0.0.1:11300');
$pheanstalk
  ->useTube("my_queue")
  ->put("Hello World");
?>

Рабочий script:

<?php
    if ($job = $pheanstalk
    ->watch('testtube')
    ->ignore('default')
    ->reserve())//retreives the job if there is one in the queue
    {
        echo $job->getData();//echos the message
        $pheanstalk->delete($job);//deletes the job from the queue
    }
}
?>

Производитель сообщений будет включен в страницу, где пользователь будет создавать сообщение, и это будет отправлено в очередь, сгенерированную beanstalkd. Рабочий script может быть спроектирован по-разному. Вы можете поместить его в цикл while для поиска новой очереди каждую секунду, и вы даже можете иметь несколько рабочих, которые ищут очередь. Pheanstalk очень эффективен и настоятельно рекомендуется.