ZeroMQ:消息消失

时间:2015-04-14 15:44:44

标签: java c# zeromq jeromq netmq

我们有一个充当服务器的Java应用程序。客户端应用程序(用C#编写)使用ZeroMQ与它通信。我们(大多数)遵循懒惰的海盗模式。

服务器有一个Router套接字,实现如下(使用JeroMQ):

ZContext context = new ZContext();
Socket socket = context.createSocket(ZMQ.ROUTER);
socket.bind("tcp://*:5555");

客户端连接并发送如下消息:

ZContext context = ZContext.Create();
ZSocket socket = ZSocket.Create(context, ZSocketType.REQ);
socket.Identity = Encoding.UTF8.GetBytes("Some identity");
socket.Connect("tcp://my_host:5555");
socket.Send(new ZFrame("request data"));

当多个客户端同时发送消息时,我们遇到了丢失的消息。对于单个客户,似乎没有任何问题。

我们是否以正确的方式实现多客户端 - 单服务器设置?

更新:显示此行为的客户端和服务器示例:

服务器:

import org.zeromq.ZContext;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.PollItem;
import org.zeromq.ZMQ.Poller;
import org.zeromq.ZMQ.Socket;
import org.zeromq.ZMsg;

public class SimpleServer
{
    public static void main(String[] args) throws InterruptedException
    {
        ZContext context = new ZContext();
        Socket socket = context.createSocket(ZMQ.ROUTER);
        socket.setRouterMandatory(true);
        socket.bind("tcp://*:5559");

        PollItem pollItem = new PollItem(socket, Poller.POLLIN);

        int messagesReceived = 0;
        int pollCount = 0;

        while ((pollCount = ZMQ.poll(new PollItem[]{pollItem}, 3000)) > -1)
        {
            messagesReceived += pollCount;

            for (int i = 0 ; i < pollCount ; i++)
            {
                ZMsg msg = ZMsg.recvMsg(socket);
                System.out.println(String.format("Received message: %s. Total messages received: %d", msg, messagesReceived));
            }

            if (pollCount == 0)
            {
                System.out.println(String.format("No messages on socket. Total messages received: %d", messagesReceived));
            }
        }
    }
}

客户端:

using NetMQ;
using System;
using System.Text;

namespace SimpleClient
{
    class Program
    {
        static byte[] identity = Encoding.UTF8.GetBytes("id" + DateTime.UtcNow.Ticks);

        static void Main(string[] args)
        {
            for (int i = 0; i < 100; i++)
            {
                SendMessage();
            }
        }

        private static void SendMessage()
        {
            using (NetMQContext context = NetMQContext.Create())
            {
                using (NetMQSocket socket = context.CreateRequestSocket())
                {
                    socket.Options.Identity = identity;
                    socket.Connect("tcp://localhost:5559");
                    socket.Send(Encoding.UTF8.GetBytes("hello!"));
                }
            }
        }
    }
}

如果我运行服务器和单个客户端,我可以看到所有100条消息到达。如果我同时运行5个客户端,我只会得到大约200个 - > 300条消息到达,而不是全部500条。顺便说一句,似乎关闭客户端中的套接字会以某种方式阻止服务器上的路由器套接字短暂接收消息,尽管这只是一种理论。

3 个答案:

答案 0 :(得分:2)

第1部分 - 民意调查可能会返回多个事件

ZMQ.poll()返回找到的事件数:

int rc = ZMQ.poll(new PollItem[]{pollItem}, 3000);

您目前假设从poll返回一个是一个事件。相反,您应该循环ZMsg msg = ZMsg.recvMsg(socket);以获取ZMQ.Poll()返回指示的事件数。

From the source of JeroMQ

/**
 * Polling on items. This has very poor performance.
 * Try to use zmq_poll with selector
 * CAUTION: This could be affected by jdk epoll bug
 *
 * @param items
 * @param timeout
 * @return number of events
 */
public static int zmq_poll(PollItem[] items, long timeout)
{
    return zmq_poll(items, items.length, timeout);
}

第2部分 - ZMsg.receive()可能会返回多个框架

当您从ZMsg收到ZMsg msg = ZMsg.recvMsg(socket);时,ZMsg可能包含多个ZFrame,每个 * // Receive message from ZMQSocket "input" socket object and iterate over frames * ZMsg receivedMessage = ZMsg.recvMsg(input); * for (ZFrame f : receivedMessage) { * // Do something with frame f (of type ZFrame) * } 都包含客户数据。

来自ZMsg class in JeroMQ's source的评论:

 * The ZFrame class provides methods to send and receive single message
 * frames across 0MQ sockets. A 'frame' corresponds to one underlying zmq_msg_t in the libzmq code.
 * When you read a frame from a socket, the more() method indicates if the frame is part of an
 * unfinished multipart message.

第3部分 - 消息可以跨多个ZFrame分割

From ZFrame's source in JeroMQ

ZMsg.receive()

如果我理解正确,那么对于每个事件,您可能会获得多个帧,并且一个客户端消息可能映射到1..N帧(如果消息很大?)。

总结一下:

  • 民意调查的一次回复可能表示多个事件。
  • 一个事件,因此一个{{1}}可能包含多个框架
  • 一个帧可以包含一个完整的客户端消息或仅包含客户端消息的一部分;一个客户端消息映射到1..N帧。

答案 1 :(得分:2)

不幸的是,我们无法解决这一特定问题,并且已经不再使用ZeroMQ来实现此接口。如果它帮助其他任何人,我们唯一明确的事情是快速打开/关闭请求套接字导致路由器套接字端的不良行为(丢失的消息)。性能不佳的服务器CPU加剧了这个问题,当服务器在快速多核机器上时根本没有出现。

答案 2 :(得分:0)

不幸的是,在这个问题发生时,我甚至还没有与ZMQ紧密合作。但是我今天遇到了同样的问题,找到了此页面。您的回答(不使用ZMQ)对我来说并不令人满意。因此,我进行了更多搜索,终于找到了解决方法。

提醒一下:这适用于ZMQ [1]中的“ POLLER”

如果您使用“ PAIR”连接,则可以肯定不会丢失任何文件,发送/接收大约需要花费时间。同时。所以你不能加快速度,对我来说不是解决方案。

解决方案:

  • 在zmq_setsockopt(python:zmq.setsockopt)中,您可以将ZMQ_HWM(zmq.SNDHWM,zmq.RCVHWM)设置为“ 0” [2]

    • 在python中: sock.setsockopt(zmq.SNDHWM,0)分别。 sock.setsockopt(zmq.RCVHWM,0)代表发件人。接收器

    • 注意:我认为符号从HWM更改为SNDWHM / RCVHWM

    • HWM = 0表示消息数没有“限制”(请注意,也许设置一个(非常高的)限制)

  • 还有ZMQ_SNDBUF / ZMQ_RCVBUF(python: zmq.SNDBUF / zmq.RCVBUF ),例如。 sock.setsockopt(zmq.RCVBUF,0)。 ..... [2]

    • 因此这会将操作系统“ SO_RCVBUF”设置为默认值(我的知识到此结束)

    • 设置此参数或不影响我的情况,但我认为可能会

性能:

因此,我可以在约8秒内(约10GB)“发送”带有98kB的100'000个文件:这将填满您的RAM(如果已满,我认为您的程序会变慢),请参见图片

同时,我“收到”文件并将其保存到约{enter image description here 118s并再次释放RAM。

此外,到目前为止,我 NERVER 丢失了一个文件。 (如果您达到了PC的极限,则可能会出现这种情况)

数据丢失为“良好”:

  • 如果确实需要所有数据,则应使用此方法

  • 如果您认为有些损失是可以的(例如现场绘图:只要您的FPS>〜50,您就可以顺利看到这些绘图,并且您不在乎是否丢失了某些东西)

  • ->您可以节省RAM并避免阻塞整个PC!

希望这篇文章对下一个来的人有帮助...

[1]: https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/multisocket/zmqpoller.htm

[2]: http://api.zeromq.org/2-1:zmq-setsockopt

您找到她的RAM图片: RAM is loading in about 8s. Afterwords the disk is saving the files from the buffer