长时间间隔后,ZMQ订户不会从远程服务器接收消息

时间:2017-06-29 13:42:38

标签: php sockets zeromq publish-subscribe pyzmq

当两个php在同一服务器上运行时,以下pubsub php正常工作。但是,它们在不同的服务器上运行时不起作用。

(local)client.php <--> (local)server.php (with sleeptime 300) <= OK
(local)client.php <--> (remote)server.php (with sleeptime 300) <= NG

我把睡眠时间从300改为30.确实有效。

(local)client.php <--> (remote)server.php (with sleeptime 30) <= OK

我认为套接字已关闭或仅在远程访问期间发生了某些事情。我该如何解决?我想检测关闭的套接字并重新连接。或者我想在可能的情况下更改超时设置。

server.php

<?php
$context = new ZMQContext();
$publisher = new ZMQSocket($context, ZMQ::SOCKET_PUB);
$publisher->bind("tcp://*:5563");

while (true) {
    sleep (300);
    $publisher->send("A", ZMQ::MODE_SNDMORE);
    $publisher->send("We don't want to see this");
    $publisher->send("B", ZMQ::MODE_SNDMORE);
    $publisher->send("We would like to see this");
}

client.php

<?php

$context = new ZMQContext();
$subscriber = new ZMQSocket($context, ZMQ::SOCKET_SUB);
$subscriber->connect("tcp://localhost:5563"); // This is the setting for local server.php. I changed it for server.php at remote.php 
$subscriber->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, "B");

while (true) {
    $address = $subscriber->recv();
    $contents = $subscriber->recv();
    printf ("[%s] %s%s", $address, $contents, PHP_EOL);

我使用的是libzmq,php-zmq。 ZMQ :: LIBZMQ_VER是4.2.1。 PHP是第5版。

我不确定,但服务器设置(ubuntu16 LTS)可能是原因。

1 个答案:

答案 0 :(得分:2)

我在PHP中使用ZeroMQ进行时间敏感的实时信令。首先,一些理论:互联网工程任务组(IETF)Request for Comments (RFC) 793RFC 1122指定TCP。在这种情况下,感兴趣的属性是用户超时。用户超时最好在RFC 5482中解释,这是一个实现在应用程序之间传递用户超时设置标准的提案:

  

&#34;传输控制协议(TCP)规范[RFC0793]定义了本地,每个连接&#34;用户超时&#34;参数   指定传输数据可能保留的最长时间   TCP之前未确认将强行关闭相应的   连接。应用程序可以使用OPEN设置和更改此参数   和发送电话。如果端到端连接中断持续时间更长   比用户超时,发件人将不会收到任何确认   任何传输尝试,包括保持活动,它将关闭   发生用户超时时的TCP连接...

     

在没有应用程序指定的用户超时的情况下,TCP   规范[RFC0793]定义默认用户超时为5分钟。   主机要求RFC [RFC1122]通过改进此定义   引入两个阈值R1和R2(R2> R1),控制   单个段的重新传输尝试次数。它表明   当达到R1时,TCP应该通知应用程序   段,并在达到R2时关闭连接。 [RFC1122]也   定义R1(3次重传)和R2(100次)的推荐值   秒),注意到SYN段的R2应该至少为3   分钟&#34;

服务器和客户端之间可能存在大量设备;你不太可能控制所有这些(当然除非LAN)。由于目前没有就应用程序和网关的超时设置达成一致的标准;和防火墙are reported to close connections,最好发送&#39;保持活力&#39;定期数据。此外,许多其他问题可能会破坏连接,同样,应用程序可能会失败。因此,保持活动的心跳实现有助于创建一个强大的应用程序,使连接保持打开状态并及时发现问题。

心跳

我通常使用1到5秒的心跳间隔,具体取决于应用程序。如果信令关闭(例如,回退到AJAX轮询,警告用户,重新启动脚本等),客户端会计算错过的心跳并相应地做出响应。请记住,发布者 - 订阅者消息不会排队。如果用户错过了它,它会错过它。如果您希望服务器能够确认收据和客户端连接状态,则需要其他通信过程。如果客户端在任何时间内停机,您无法确定是否收到了所有消息。 ZeroMQ Guide has substantial discussion about heartbeating和强大的应用程序设计。

  

&#34; Heartbeating解决了知道同伴是否还活着的问题   死。这不是ZeroMQ特有的问题。 TCP有很长的超时时间   (30分钟左右),这意味着无法知道   一个同伴是否已经死亡,被断开连接,或者周末去了   布拉格有一箱伏特加,一个红头发和一个大笔费用帐户。&#34;

好的,这是使用您的代码的代码示例:

<强> Server.php

<?php

$context = new ZMQContext();
$publisher = new ZMQSocket($context, ZMQ::SOCKET_PUB);
$publisher->bind("tcp://*:5563");

$i = 0; // Iteration counter to trigger message

while (true) {

    $publisher->send('HB'); // Distribute the heartbeat (all clients will subscribe)

    sleep (5); // Heartbeat interval

    $i++ // Clock that ticks up ~5 seconds at a time (alternatively,
    // you could set a variable to store the timestamp of the last time the 
    // messages (A & B) were sent and then check to see if 5 minutes has elapsed).

    if ($i >= 60) {
        // 300 seconds--or perhaps a hair more--have elapsed, send the messages

        $i = 0; // Rest the clock.

        $publisher->send("A", ZMQ::MODE_SNDMORE);
        $publisher->send("We don't want to see this");
        $publisher->send("B", ZMQ::MODE_SNDMORE);
        $publisher->send("We would like to see this");
    } // Don't forget to close if statement.    
}

<强> Client.php

<?php

$context = new ZMQContext();
$subscriber = new ZMQSocket($context, ZMQ::SOCKET_SUB);
$SERVERIP = 'Where is the server?'; // Make sure this is correct and no firewall!
$subscriber->connect("tcp://$SERVERIP:5563"); 

$subscriber->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, "B");
$subscriber->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, 'HB'); // Subscribe to the heartbeat

$heartMonitor = time(); // Time we started listening.

while (true) {
    $address = $subscriber->recv();

    $more = $subscriber->getSockOpt(ZMQ::SOCKOPT_RCVMORE); // See if this is a multipart message
    if ($more) {
    // Message has second part--normally there would be a loop for variable length messages.
    $contents = $subscriber->recv();
    }

    if ($address == 'HB') {
        // Message is the heartbeat, would send this to additional
        // application components and have them listen for heartbeat as well.
        $heartMonitor = time();  // Time last heartbeat received in seconds
    }else{
        // Message is your content message; Do stuff.
        printf ("[%s] %s%s", $address, $contents, PHP_EOL);
        $heartMonitor = time();  // It doesn't matter which message was just received, both confirm connection.
    }

    $timeout = 20; // At least 3 heartbeat signals were missed.

    if ($heartMonitor < (time() - $timeout)) {
        // Connection lost. Alert user, fallback, or attempt reconnect, etc. here.
        break;
    }
} // Don't forget to close while loop.

最后,有一些ZeroMQ options用于修改TCP设置;但是,我不确定这支持得多好。此外,它不像心跳那样在应用程序级别提供可靠性检查。

因此,最好保持连接打开(不要错过消息,这实际上是与其他协议相比大量延迟减少的地方);你不想重新连接很多东西。此外,没有可靠的方法来更改服务器,网络设备和客户端上的TCP设置。最好的方法是确认应用程序级别的通信。

我希望这会有所帮助并让你朝着正确的方向前进。我没有测试过这段代码,但我会尝试明天(然后编辑这篇文章)。