zeromq / netmq

A 100% native C# implementation of ZeroMQ for .NET
Other
2.95k stars 744 forks source link

Messages being missed by Router to Router setup #791

Closed sin0emp closed 4 years ago

sin0emp commented 5 years ago

Environment

NetMQ Version: 4.0.01
Operating System: Ubuntu 16.04
.NET Version: dotnet core 2.2

Expected behavior

Setting up a router to router client-server on a single machine, and sending a million messages from server side to the client side, it is expected to receive all the packets by the client. Giving that there is no problem with doing the same thing on a raw tcp socket.

Actual behavior

Randomly there are dropped or missed packets from the client side. Investigating further, I realized that setting the socket option ReconnectInterval to zero will cause the socket to not receive any further message after missing the first message. Hence I deduce that the problem is socket disconnection in the middle of the procedure which will result in reconnection attempts and in the meantime, the packets sent from the server will be lost. I know that zero mq is an at-most-once queue so one should expect to miss packets and be prepared for it. But my problem is that this setup is on a single machine, there is no physical network between them and there is no such problem with doing the same scenario on the underlying communication technology that is tcp socket. So, what will cause its tcp connection to be closed and then reconnect?

Steps to reproduce the behaviour

Client Code:

    var receiveList = new List<int>();
    var outOfOrders = new List<int>();
    using (var socket = new RouterSocket(">tcp://0.0.0.0:5555"))
    {
        socket.Options.Identity = Encoding.ASCII.GetBytes("client");

        var expect = 0;

        while (true)
        {
            var message = socket.ReceiveMultipartMessage();
            var received = BitConverter.ToInt32(message[2].Buffer);

            if (received == -1)
                break;

            receiveList.Add(received);

            if (expect != received)
                outOfOrders.Add(expect);

            expect = received + 1;
        }
    }

    Console.WriteLine($"Received {receiveList.Count} items");
    foreach (var item in outOfOrders)
        Console.WriteLine($"{item} was out of order");

Server Code:

    using (var socket = new RouterSocket("@tcp://0.0.0.0:5555"))
    {
        Thread.Sleep(1000);
        for (var i = 0; i < 1000000; ++i)
        {
            socket.SendMoreFrame(Encoding.ASCII.GetBytes("client")).SendMoreFrameEmpty()
                .SendFrame(BitConverter.GetBytes(i));
        }

        socket.SendMoreFrame(Encoding.ASCII.GetBytes("client")).SendMoreFrameEmpty()
            .SendFrame(BitConverter.GetBytes(-1));
    }

Running client, then server, client gives an output like the following (missing 165 messages).

Received 999835 items
130500 was out of order
162004 was out of order
620044 was out of order
sin0emp commented 5 years ago

UPDATE

Setting the socket option RouterMandatory on the server side, will solve the problem! Reading the description of this option, this option is related to cases when router fails to route the message. The original question still holds that, is why router is failing to deliver (why the connection is lost?). Another thing is that, it should only throw an exception in face of failing to route a message. So how come it affects the behavior of the above code? Is the description incomplete?

Set the RouterSocket behavior when an unroutable message is encountered. A value of false is the default and discards the message silently when it cannot be routed. A value of true causes throw of HostUnreachableException if the message cannot be routed.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had activity for 365 days. It will be closed if no further activity occurs within 56 days. Thank you for your contributions.