Search code examples
c#.netzeromqnetmq

Using NetMQMonitor to detect server disconnects?


I am looking for a better way to detect disconnects when a Router/server goes down or is unavailable due to a poor connection. (I'm Listening from a Dealer/client running on wifi) I found zmq_socket_monitor() and discovered that NetMQ has the same feature. My understanding from the documentation is that when you monitor a socket you give it an inproc address, and it notifies you of any socket changes using that address. I couldn't really find any examples of the NetMQMonitor except the unit tests, my question is if I am using it correctly in the code below? Is it valid to use it alongside a NetMQPoller?

        // run poller on a separate thread
        _poller = new NetMQPoller { _dealer, _subscriber, _outgoingMessageQueue, _subscriptionChanges};
        _poller.RunAsync();

        // run a monitor listening for Connected and Disconnected events
        _monitor = new NetMQMonitor(_dealer, "inproc://rep.inproc", SocketEvents.Disconnected | SocketEvents.Connected);
        _monitor.EventReceived += _monitor_EventReceived;
        _monitor.StartAsync();

**** UPDATE ****


So... after posting this I discovered the answer in the NetMQPoller tests on github, so that answers whether you can use the NetMQMonitor with a NetMQPoller, but I'm still curious if anyone has thoughts on the overall approach of using a monitor to track connection state. Here's the relevant code for anyone interested:

    [Fact]
    public void Monitoring()
    {
        var listeningEvent = new ManualResetEvent(false);
        var acceptedEvent = new ManualResetEvent(false);
        var connectedEvent = new ManualResetEvent(false);

        using (var rep = new ResponseSocket())
        using (var req = new RequestSocket())
        using (var poller = new NetMQPoller())
        using (var repMonitor = new NetMQMonitor(rep, "inproc://rep.inproc", SocketEvents.Accepted | SocketEvents.Listening))
        using (var reqMonitor = new NetMQMonitor(req, "inproc://req.inproc", SocketEvents.Connected))
        {
            repMonitor.Accepted += (s, e) => acceptedEvent.Set();
            repMonitor.Listening += (s, e) => listeningEvent.Set();

            repMonitor.AttachToPoller(poller);

            int port = rep.BindRandomPort("tcp://127.0.0.1");

            reqMonitor.Connected += (s, e) => connectedEvent.Set();

            reqMonitor.AttachToPoller(poller);

            poller.RunAsync();

            req.Connect("tcp://127.0.0.1:" + port);
            req.SendFrame("a");

            rep.SkipFrame();

            rep.SendFrame("b");

            req.SkipFrame();

            Assert.True(listeningEvent.WaitOne(300));
            Assert.True(connectedEvent.WaitOne(300));
            Assert.True(acceptedEvent.WaitOne(300));
        }
    }

Solution

  • Using the monitor is exactly the right way to look for changes in connection state.

    Under the hood the management threads are ping pinging across the connection. If the ping pongs dry up, then there is a problem. This detects network issues, but also detects things like crashes; if the process at one of a socket dies, the process at the other end is informed of the dead connection.

    The only inadequacy is if it matters to you what happens to sent messages. Different sockets cache messages in different places, some being biased to keeping them at the sending end until the receiver is ready, others storing them at the receiving end. If the connection dies and you want your undelivered messages back (to send elsewhere, perhaps), you can't get them. ZMQ is like a post office. As soon as you hand the letter over the counter, they cannot and will not give it back to you, even if you can still see it!

    This is the nature of Actor model, which is what ZMQ implements. Communicating Sequential Processes, a development of Actor model, does not store messages in a channel at all, meaning that if the connection dies the application still owns the unsent message. Sometimes it's useful to know for sure if a message definitely was not delivered.