1

We have recently started working on Typescript language for one of the application where a queue'd communication is expected between a server and client/clients.

For achieving the queue'd communication, we are trying to use the ZeroMQ library version 4.6.0 as a npm package: npm install -g zeromq and npm install -g @types/zeromq.

The exact scenario :

The client is going to send thousands of messages to the server over ZeroMQ. The server in-turn will be responding with some acknowledgement message per incoming message from the client. Based on the acknowledgement message, the client will send next message.

ZeroMQ pattern used :

The ROUTER/DEALER pattern (we cannot use any other pattern).

Client side code :

    import Zmq  = require('zeromq');
    let clientSocket : Zmq.Socket;
    let messageQueue = [];

    export class ZmqCommunicator
    {
        constructor(connString : string)
        {
           clientSocket = Zmq.socket('dealer');
           clientSocket.connect(connString);
           clientSocket.on('message', this.ReceiveMessage);
        }

        public ReceiveMessage = (msg)  => {
            var argl = arguments.length,
            envelopes = Array.prototype.slice.call(arguments, 0, argl - 1),
            payload = arguments[0];
            var json = JSON.parse(msg.toString('utf8'));

            if(json.type != "error" && json.type =='ack'){
                if(messageQueue.length>0){
                  this.Dispatch(messageQueue.splice(0, 1)[0]);
            }
        }

        public Dispatch(message) {
              clientSocket.send(JSON.stringify(message));
        }

        public SendMessage(msg: Message, isHandshakeMessage : boolean){
             // The if condition will be called only once for the first handshake message. For all other messages, the else condition will be called always. 
             if(isHandshakeMessage == true){
                clientSocket.send(JSON.stringify(message));
             }
             else{ 
                 messageQueue.push(msg);
            }
        }
  }

On the server side, we already have a ROUTER socket configured. The above code is pretty straight forward. The SendMessage() function is essentially getting called for thousands of messages and the code works successfully but with load of memory consumption.

Problem :

Because the behavior of ZeroMQ is asynchronous, the client has to wait on the call back call ReceiveMessage() whenever it has to send a new message to ZeroMQ ROUTER (which is evident from the flow to the method Dispatch).

Based on our limited knowledge with TypeScript and usage of ZeroMQ with TypeScript, the problem is that because default thread running the typescript code (which creates the required 1000+ messages and sends to SendMessage()) continues its execution (creating and sending more messages) after sending the first message (handshake message essentially), unless all the 1000+ messages are created and sent to SendMessage() (which is not sending the data but queuing the data as we want to interpret the acknowledgement message sent by the router socket and only based on the acknowledgement we want to send the next message), the call does not come to the ReceiveMessage() call back method.

It is to say that the call comes to ReceiveMessage() only after the default thread creating and calling SendMessage() is done doing this for 1000+ message and now there is no other task for it to do any further.

Because ZeroMQ does not provide any synchronous mechanism of sending/receiving data using the ROUTER/DEALER, we had to utilize the queue as per the above code using a messageQueue object.

This mechanism will load a huge size messageQueue (with 1000+ messages) in memory and will dequeue only after the default thread gets to the ReceiveMessage() call at the end. The situation will only worsen if say we have 10000+ or even more messages to be sent.

Questions :

  1. We have validated this behavior certainly. So we are sure of the understanding that we have explained above. Is there any gap in our understanding of either/or TypeScript or ZeroMQ usage?

  2. Is there any concept like a blocking queue/limited size array in Typescript which would take limited entries on queue, and block any new additions to the queue until the existing ones are queues (which essentially applies that the default thread pauses its processing till the time the call back ReceiveMessage() is called which will de-queue entries from the queue)?

  3. Is there any synchronous ZeroMQ methodology (We have used it in similar setup for C# where we pool on ZeroMQ and received the data synchronously)?.

  4. Any leads on using multi-threading for such a scenario? Not sure if Typescript supports multi threading to a good extent.

Note : We have searched on many forums and have not got any leads any where. The above description may have multiple questions inside one question (against the rules of stackoverflow forum); but for us all of these questions are interlinked to using ZeroMQ effectively in Typescript.

Looking forward to getting some leads from the community.

user3666197
  • 1
  • 6
  • 50
  • 92

1 Answers1

0

Welcome to ZeroMQ

If this is your first read about ZeroMQ, feel free to first take a 5 seconds read - about the main conceptual differences in [ ZeroMQ hierarchy in less than a five seconds ] Section.


1 ) ... Is there any gap in our understanding of either/or TypeScript or ZeroMQ usage ?

Whereas I cannot serve for the TypeScript part, let me mention a few details, that may help you move forwards. While ZeroMQ is principally a broker-less, asynchronous signalling/messaging framework, it has many flavours of use and there are tools to enforce both a synchronous and asynchronous cooperation between the application code and the ZeroMQ Context()-instance, which is the cornerstone of all the services design.

The native API provides means to define, whether a respective call ought block, until a message processing across the Context()-instance's boundary was able to get completed, or, on the very contrary, if a call ought obey the ZMQ_DONTWAIT and asynchronously return the control back to the caller, irrespectively of the operation(s) (in-)completion.

As additional tricks, one may opt to configure ZMQ_SND_HWM + ZMQ_RCV_HWM and other related .setsockopt()-options, so as to meet a specific blocking / silent-dropping behaviours.


Because ZeroMQ does not provide any synchronous mechanism of sending/receiving data

Well, ZeroMQ API does provide means for a synchronous call to .send()/.recv() methods, where the caller is blocked until any feasible message could get delivered into / from a Context()-engine's domain of control.

Obviously, the TypeScript language binding/wrapper is responsible for exposing these native API services to your hands.


3 ) Is there any synchronous ZeroMQ methodology (We have used it in similar setup for C# where we pool on ZeroMQ and received the data synchronously) ?

Yes, there are several such :
- the native API, if not instructed by a ZMQ_DONTWAIT flag, blocks until a message can get served
- the native API provides a Poller()-object, that can .poll(), if given a -1 as a long duration specifier to wait for sought for events, blocking the caller until any such event comes and appears to the Poller()-instance.

Again, the TypeScript language binding/wrapper is responsible for exposing these native API services to your hands.


... Large memory consumption ...

Well, this may signal a poor resources management care. ZeroMQ messages, once got allocated, ought become also free-d, where appropriate. Check your TypeScript code and the TypeScript language binding/wrapper sources, if the resources systematically get disposed off and free-d from memory.

user3666197
  • 1
  • 6
  • 50
  • 92