带有PHP的ZeroMQ Pub/sub在单个web连接上失败,但可以使用循环


ZeroMQ Pub/sub with PHP fails on single web connection but works with loop

我在PHP中有一个ZeroMQ pub/sub设置的简单代码,奇怪的是它不适用于单个(1x)web请求,当我注释掉while(true){//code}时,它不起作用,web客户端不会收到单个请求,但当在循环中调用publisher->send()时,web客户端会正常接收所有消息。

ZeroMQ PHP发布服务器

    <?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache'); // recommended to prevent caching of event dat
    header("Connection: keep-alive");
    $port=8899; 
    //  Prepare our context and publisher
    $context = new ZMQContext();
    $publisher = $context->getSocket(ZMQ::SOCKET_PUB);
    $publisher->bind("tcp://*:".$port);
    echo "Running..Serving test data on TCP port $port every 1 s 'n <hr>";
    while (true) {  //doesn't work when this line is commented out
        $count++;
        $json=json_encode(array($count, PHP_RELEASE_VERSION, PHP_OS , $_SERVER['PHP_SELF'], Date("h:i:s m-d-Y")  ) );
        $publisher->send( $json);
        sleep(1);
        echo "+";   
        ob_flush();
       flush();  //send out to browser
     }
    ?>

ZeroMQ PHP订阅服务器

<?php
header('Cache-Control: no-cache'); // recommended to prevent caching of event dat
header("Connection: keep-alive");
$context = new ZMQContext();
$subscriber = $context->getSocket(ZMQ::SOCKET_SUB);
$subscriber->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, '');
$port=8899;
$subscriber->connect('tcp://localhost:'.$port);
while(true) {
    echo "<br>Waiting for message " ;
    $string = $subscriber->recv();
    echo "<br>Received ".$string ;
    ob_flush();
    flush();
 }
?>

我怀疑这与PHP脚本结束时ZeroMQ消息从未发送或关闭单个请求有关。。

正如他们的指南中所讨论的,该问题与发布/订阅情况下ZeroMQ的"慢加入者"症状有关

关于PUB-SUB套接字还有一件更重要的事情需要了解:不知道订阅者何时开始接收消息。即使如果启动订阅服务器,请等待一段时间,然后启动发布服务器,订阅者将始终错过发布者发送的第一条消息发送这是因为当订阅者连接到发布者时(需要很小但非零时间的东西),发布者可能已经在发送消息。

上面代码中的问题都在ZeroMQ PHP Publisher脚本上。。基本上是因为ZeroMQ设置Socket和完成它的准备工作所需的时间(很小但非零的时间),所以当发布者第一次设置MQ.时会有一个小的延迟

棘手的部分是它在调用后立即返回

 //  Prepare our context and publisher
$context = new ZMQContext();
$publisher = $context->getSocket(ZMQ::SOCKET_PUB);
$publisher->bind("tcp://*:".$port);  //returns immediately BUT NOT REALLY ready

当然,以上PHP的其余部分将在不到10ms的时间内运行完毕,因此当您发出实际的send命令时,MQ还没有设置,并且初始消息将丢失。

   $publisher->send( $json);  //send the message but MQ not REALLY setup just yet.

这当然是造成这种混乱的原因,因为在编程中,当函数(如$publisher->bind)成功返回时,它已经完成了任务,但实际上并没有。。

简单的解决方案是在绑定后立即使用一个小延迟(您需要使用延迟值),以便ZeroMQ有机会完成其工作。现在这种方法并不是最优雅的,因为延迟量可能因机器等而异,ZeroMq的指南告诉你这一点。。

ZeroMQ的人员建议使用一种更复杂的方法来跟踪订阅者,并有一个后台通道,让加入的订阅者在加入后可以与发布者通信。。

出于我的目的,这种方法过于夸张,所以下面的命令运行良好,只需给usleep值足够的时间。。

  $port=8899; 
//  Prepare our context and publisher
$context = new ZMQContext();
$publisher = $context->getSocket(ZMQ::SOCKET_PUB);
$publisher->bind("tcp://*:".$port);
usleep(250000);   //wait 0.25 secs enougth for ZeroMQ to build the socket.

这对我很有效…