ZeroMQ отправка / получение на PHP работает, если скрипт запускается из CLI / оболочки, но не из APACHE / веб-запроса - PullRequest
0 голосов
/ 10 октября 2019

Я тестирую ZeroMQ для PHP. Моя цель - отправлять сообщения скрипту Python. Все работает нормально, если я запускаю свой скрипт передачи из PHP cli

php /path/to/myscript.php

, в то время как происходит сбой, если это веб-запрос. Я попытался выполнить серверный скрипт из PHP cli, как описано выше (что представляется более логичным способом) и с помощью веб-запроса.

У меня есть сервер Centos 7 с PHP 7.2 и ZeroMQ 1.1. 3. Устанавливается через PECL.

Я даже пытался запустить указанную выше команду с shell_exec / exec внутри клиентского скрипта, но она не работает. Связь работает нормально, но не отправляет и не получает.

Код клиента:

$context = new ZMQContext();

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


for ($request_nbr = 0; $request_nbr != 10; $request_nbr++) {
    printf ("Sending request %d...\n", $request_nbr);
    $risSend = $requester->send("Hello", ZMQ::MODE_NOBLOCK);
    print_r($risSend);
    $reply = $requester->recv();
    printf ("Received reply %d: [%s]\n", $request_nbr, $reply);
}

Код сервера:

$context = new ZMQContext(1);

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

while (true) {
    //  Wait for next request from client
    $request = $responder->recv();

    printf ("Received request: [%s]\n", $request);

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

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

Ответы [ 2 ]

1 голос
/ 10 октября 2019

НАБЛЮДЕНИЕ : Браузер зависает безо всяких ошибок.

Это вполне законное состояние. Чтобы это произошло, вполне достаточно «пропустить» поступление первого REQ уже отправленного запроса и из-за удовольствия зависит от распределенного конечного состояния автомата,мы попадаем в незаменимый тупик, где сторона REQ ждет ответа, который никогда не придет (см. далее), а сторона REP ждет запроса, который никогда не поступит (см. REQ сторона уже ждет) и такое состояние остается навсегда.


Лучший следующий шаг:

В случае, если никто никогда не работал с ZeroMQ,
илиникогда не встречал концепцию искусства Zen-of-Zero ,
, здесь можно с первого взгляда взглянуть на "ZeroMQ Принципы менее чем за пять секунд ", прежде чем углубляться в детали


Старт
с безоговорочно работающими архетипами - пара PUSH / PULL симплекс-каналов, которые не требуют двухэтапного dFSA REQ-REP-REQ-REP-REQ-REP-...-{deadlock} ... принципиально неизбежного терминимальное состояние, в котором просто никогда не уверен , когда это произойдет, но это произойдет ... через некоторое время: o)

Далее,
может повысить надежность потока сообщений, используя zmq_setsockopt( ZMQ_IMMEDIATE, 1 ), что позволяет избежать перемещения сообщений по неполным соединениям между / между узлами.

Всегда
предпочитают неблокирующие формы .recv() -методов, лучше всего с предварительным тестированием присутствия сообщения с .poll() -методом. Poller -класс, хотя и доступен во многих языковых привязках, не всегда так удобен и гибок, как использование явного .poll() -метода непосредственно в Socket -экземпляре.

Также не стесняйтесь читать больше о тонкой настройке инструментов ZeroMQ и других значениях Искусства Zen-of-Zero здесь .


Макет на стороне сервера: As {PASS |FAIL} -защищенная от .send()---.recv() цепочка доставки работает?

<?php                                      /* Create new PUSH-ing end */
$aCTX   = new ZMQContext();
try {                                      /* Try: things may turn wreck havoc */

      $PUSHer = $aCTX->getSocket(, ZMQ::SOCKET_PUSH );
      echo "POSACK'd: .getSocket() was made\n";
      }
catch ( ZMQSocketException $e ){
      echo "  NACK'd: I told you ...\n";   /* Handle with care ... */
      if ( $e->getCode() === ZMQ::ERR_... ) {
            echo " - Got ERR_..., read ZeroMQ API documentation for details\n";
        } else {
            die( " - Get ERR: " . $e->getMessage() );
        }
      }
try {                                      /* Try: things may turn wreck havoc */
      $PUSHer->bind( "tcp://A.B.C.D:NNN" ); /* IP address to .connect() */
      echo "POSACK'd: .bind() was made\n";
      }
catch ( ZMQSocketException $e ){
      echo "  NACK'd: I told you ...\n";   /* Handle with care ... */
      if ( $e->getCode() === ZMQ::ERR_... ) {
            echo " - Got ERR_..., read ZeroMQ API documentation for details\n";
        } else {
            die( " - Get ERR: " . $e->getMessage() );
        }
      }

$retries = 1234567;

do {                                       /* Start a loop */
    try {                                  /* Try: to PUSH.send() */
            echo "Trying to send a message #" . ( 1234568 - $retries ) . "\n";
            $PUSHer->send( "This is a message", ZMQ::MODE_DONTWAIT );
            echo "POSACK'd: PUSHer.send() was made\n";
        }
    } catch ( ZMQSocketException $e ) {
        echo "  NACK'd: I told you ...\n"; /* Handle with care ... */
        if ( $e->getCode() === ZMQ::ERR_... ) {
            echo " - Got ERR_..., read ZeroMQ API documentation for details\n";
        } else {                           /* For all ERR_... states */
            die( " - Got ERR_...: " . $e->getMessage() );
        }
    }
 /* --------------------------------------------------------------------
    Here one may add an attempt to .recv( $PULLer, ZMQ::MODE_DONTWAIT );
             and test for a non-empty string returned
    -------------------------------------------------------------------- */
    usleep( 1 );                           /* Sleep a bit between operations */
} while ( --$retries );
?>

Макет на стороне клиента , чтобы проверить жизни PUSH-эра и .send() -s

import time, datetime, zmq; print( "Thissssss Sssssssssssssssssssssssssssssssssssssssnake uses ZeroMQ ver:{0:}".format( zmq.__version__ ) )

aCtx = zmq.Context()
aPull= aCtx.Socket( zmq.PULL )
aPull.setsockopt(   zmq.LINGER, 0 )         # always ... be explicit
aPull_address2c = "tcp://A.B.C.D:NNN"

M0 = "{0:} try a .connect( {1:} ), if it gets to PUSH-er side"
M1 = "{0:} try a .recv(), if it gets any message"
M2 = "{0:} got a .recv()->[[[ {1:} ]]]"
M3 = "{0:} EXC'd           will gracefully release resources and terminate..."
M4 = "{0:} did"

try:
    print( M0.format( datetime.datetime.isoformat( datetime.datetime.now() ),
                      aPull_address2c
                      )
           )
    aPull.connect( aPull_address2c );

    while True:
        print( M1.format( datetime.datetime.isoformat( datetime.datetime.now() ) )
        m = aPull.recv( zmq.NOBLOCK )       # always ... avoid blocking waits
        if ( len( m ) > 0 ):
             print( M2.format( datetime.datetime.isoformat( datetime.datetime.now() ),
                               str( m )     # always ... fused to str()
                               )
                    )
             time.sleep( 5 )
        else:
             time.sleep( 1 )

        pass

        ################################################################
        # Here one may add an attempt to aPush.send( M4, zmq.NOBLOCK )
        #          and test if the reverse path aPush->$PULLer goes well
        ################################################################

except:
    print( M3.format( datetime.datetime.isoformat( datetime.datetime.now() ) )

finally:
    aPull.close()                           # always ... be explicit
    aCtx.term()                             # always ... be explicit

    print( M4.format( datetime.datetime.isoformat( datetime.datetime.now() ) )

0 голосов
/ 14 октября 2019

Хорошо, я наконец нашел решение. Благодаря @ user3666197 мне удалось получить ошибку. И это была ошибка «Отказано в доступе».

В CentOS (и, вероятно, во всех системах Linux с SELinux) вы должны добавить правило для порта, используемого ZMQ для веб-сервера .

Пример:

semanage port -a -t http_port_t -p tcp 5555
...