ZeroMQ - Quick Guide



ZeroMQ - Overview

In this chapter, we are dedicated to knowing the details of ZeroMQ, such as its definition, usage, history, inventor, and development. Let us understand each section one by one.

What is ZeroMQ?

TheZeroMQis a lightweight open-source universal messaging library that enables asynchronous communication between distributed systems, applications, and services. It supplies a simple socket-like API which is used to create scalable, concurrent, and fault-tolerant architectures. It is written in C++.

Asynchronous communication between distributed systems: It is a method that works for message exchange where the sending and receiving processes operate independently and do not need to wait for each other to complete their tasks.

ZeroMQ supports common messaging patterns such as pub or sub, request or reply, and client or server. It also supports various protocols like TCP, in-process, inter-process, multi-cast, and web socket.

ZeroMQ Use Cases

It can be used as the fabric for the cluster products and is designed to simplify the development of the complex messaging pattern.

Who invented ZeroMQ?

ZeroMQ is an universal messaging library discovered in 2007 by Belgian software engineer Pieter Hintjens. Hintjens's goal in building ZeroMQ was to create a flexible, high-performance and easy-to-use messaging library. ZeroMQ has had a great role on the future of asynchronous communication, not only due to its architecture, but also because of Hintjens continuous improvement of the ZeroMQ library.

History & Evolution

Following is the history and evolution of ZeroMQ −

Early Days: 2007 - 2009

  • ZeroMQ was invented in 2007 by Pieter Hintjens, a Belgian software developer.
  • Initially, it was designed by iMatrix Corporation, a software company founded by Hintjens.

Rapid Growth: 20102012

  • ZeroMQ gained popularity in the 2010s as a messaging library for distributed systems and cloud computing.
  • It attracted a large community of contributors, and the library supported several programming languages, including Python, Java, and C-Sharp.
  • ZeroMQ 2.0 was launched in 2010, introducing a new API and improved performance.

Founding of the ZeroMQ Organization: 2011

  • The ZeroMQ organization was formed in 2011 to design and oversee the library.
  • The organization established a governance model that ensured the project remained community-driven and open-source.

Expansion and Maturity: 2013 - 2016

  • ZeroMQ 3.0 was launched in 2012, including a new security framework and improved support for WebSockets.
  • The library continued to gain popularity with support from the following industries: Finance, Healthcare, and IoT.
  • The ZeroMQ organization joined the Linux Foundation in 2014, ensuring the long-term sustainability of the project.

Recent Development: 2017 - Present

  • ZeroMQ 4.0 was launched in 2017, featuring improved performance, security, and support for new protocols.
  • ZeroMQ remains a popular choice for building distributed systems, cloud applications, and Internet of Things (IoT) solutions.

Throughout its development process, ZeroMQ has focused on ease of use, scalability, and flexibility, contributing to its support as a framework for building distributed systems, cloud applications, and IoT solutions.

ZeroMQ - Installation Setup

First step in the journey of learning ZeroMQ is to install the core ZeroMQ library on your system, and depending on the programming language you are using, you need to install the corresponding language bindings to interact with ZeroMQ in your code.

For example, if you are using Python language, you need to install the pyzmq, which provides the Python bindings for ZeroMQ.

ZeroMQ library is not dependent on the operating system for installation. Instead, it depends on the programming language and its respective binding dependencies. Therefore, the installation steps are generally similar across different operating systems.

How to setup ZeroMQ in Java?

The following instructions will explain how to install and set up the ZeroMQ library in Java. To install and use ZeroMQ (MQ) in Java, you need to use a Java binding for ZeroMQ. The most commonly used binding is JeroMQ, which is a pure Java implementation of ZeroMQ.

Here is the step-by-step guide to install ZeroMQ in Java −

Step 1: Create a Maven Project in Java

To create a Maven Project in Java, open your preferred IDE (we are using Eclipse), click on the File menu on the left-top corner, and choose Maven Project in the displayed list, as shown in the following image −

ZeroMQ Installation

This will give you a "New Maven Project" window. In it, click on the Next button.

ZeroMQ Installation

In the next window, enter "Apache" in the Filter text box, select quickstart and click on Next.

ZeroMQ Installation

Then, enter "com.zeromq" in the GroupId text-box and "zeromq" in the ArtifactId text-box, then click Finish (You can choose any values for "GroupId" and "ArtifactId").

ZeroMQ Installation

After clicking Finish enter 'y' in your terminal and press Enter.

Step 2: Add ZeroMQ Dependency

The simplest way to install ZeroMQ in a Java project is by using JeroMQ, which can be included as a dependency in your project.

If you are using Maven or Gradle (both are Java development tools) to manage your project, add the dependency to your pom.xml or build.gradle file.

<dependency>
<groupId>org.zeromq</groupId>
<artifactId>jeromq</artifactId>
<version>0.5.3</version>
</dependency>

//for the latest SNAPSHOT -->
<dependency>
<groupId>org.zeromq</groupId>
<artifactId>jeromq</artifactId>
<version>0.6.0-SNAPSHOT</version>
</dependency>

//If you can't find the latest snapshot
<repositories>
<repository>
<id>sonatype-nexus-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>

Step 3: Write Your First ZeroMQ Program in Java

Once JeroMQ is included in your project, you can start using it. Following is an example of a basic ZeroMQ publisher and subscriber in Java −

Publisher

The following is the basic example of the "Publisher" −

import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;

public class Publisher {
   public static void main(String[] args) {
      Context context = ZMQ.context(1);
      Socket socket = context.socket(ZMQ.PUB);
      socket.bind("tcp://*:5555");

      while (true) {
         socket.send("Hello, subscribers!".getBytes(), 0);
      }
   }
}

Output

//it will simply send the message to the receiver

Subscriber

The following is the "Subscriber" example −

import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;
public class Subscriber {
   public static void main(String[] args) {
      Context context = ZMQ.context(1);
      Socket socket = context.socket(ZMQ.SUB);
      socket.connect("tcp://localhost:5555");
      socket.subscribe("".getBytes()); // Subscribe to all messages

      while (true) {
         byte[] message = socket.recv(0);
         System.out.println("Received message: " + new String(message));
      }
   }
}

Output

The above program displays all the receive messages, which is sent by "publisher" −

Received message: Hello, subscribers!
Received message: Hello, subscribers!
Received message: Hello, subscribers!
Received message: Hello, subscribers!
Received message: Hello, subscribers!
Received message: Hello, subscribers!

How to setup ZeroMQ in Python?

To Download and install ZeroMQ in Python, we need to download the Python library that binds with ZeroMQ which is PyZMQ library, which is a fast and lightweight messaging library.

PyZMQ works with any reasonable version of Python that should be greater than or equal to 3.7, as well as PyPy. PyZMQ is used for message queuing without a message broker.

Step 1: Install PyZMQ

To install the PyZMQ we can use the pip, which is the most common way to install Python packages. The following are the steps:

Open the terminal and command prompt

  • On the window, press window + R, type cmd, and hit enter.
  • On Mac or Linux, open a terminal.

Run the pip install command

To install PyZMQ, run the following command in your terminal or command prompt:

pip install pyzmq

The above command will download and install the PyZMQ and the ZeroMQ libraries.

Downloading pyzmq-26.2.0-cp312-cp312-win_amd64.whl.metadata (6.2 kB)
Downloading pyzmq-26.2.0-cp312-cp312-win_amd64.whl (637 kB)
   --------------------- 637.8/637.8 kB 5.8 MB/s eta 0:00:00
Installing collected packages: pyzmq
Successfully installed pyzmq-26.2.0

[notice] A new release of pip is available: 24.0 -> 24.2
[notice] To update, run: python.exe -m pip install --upgrade pip

Step 2: Verification

To confirm that PyZMQ is installed correctly, open the Python shell and run the following command. If it prints the installed version of ZeroMQ and PyZMQ without any errors, the installation is successful.

>>> import zmq
>>> print(zmq.zmq_version())
4.3.5
>>> print(zmq.pyzmq_version())
26.2.0
>>>

Step 3: Write your first ZeroMQ program in Python

As we have already installed the Python messaging library "PyZMQ", we can write a Python program using ZeroMQ.

Publisher

The following is the basic example of the "Publisher" −

import zmq
import time

# Create a ZeroMQ context
context = zmq.Context()

# Create a PUB socket
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:5555")

while True:
   # Send a message every second
   message = "Hello, ZeroMQ!"
   socket.send_string(message)
   print(f"Sent: {message}")
   time.sleep(1)

Output

Sent: Hello, ZeroMQ!
Sent: Hello, ZeroMQ!
Sent: Hello, ZeroMQ!
Sent: Hello, ZeroMQ!
Sent: Hello, ZeroMQ!

Subscriber

The following is the "Subscriber" example −

import zmq

# Create a ZeroMQ context
context = zmq.Context()

# Create a SUB socket
socket = context.socket(zmq.SUB)
socket.connect("tcp://localhost:5555")

# Subscribe to all messages (empty string as subscription)
socket.setsockopt_string(zmq.SUBSCRIBE, "")

while True:
   message = socket.recv_string()
   print(f"Received: {message}")

Output

Received: Hello, ZeroMQ!
Received: Hello, ZeroMQ!
Received: Hello, ZeroMQ!
Received: Hello, ZeroMQ!
Received: Hello, ZeroMQ!

ZeroMQ - Features

ZeroMQis also known as 0MQ, or ZMQ. It is a lightweight open-source messaging library, which provides a simple and organized way to implement messaging patterns in both web and mobile applications. Here, is the some of its key features −

Messaging Pattern

ZeroMQ has several messaging patterns including −

  • Request-Response: It allows a client to send a request to the server and receive a response.
  • Publish-Subscribe: It allows a publisher to send messages to multiple subscribers.
  • Push-Pull: It allows a producer to push messages to multiple consumers.
  • Pub-Sub with filtering: It allows subscribers to filter messages based on topic.

Socket Type

ZeroMQ has several socket type including:

  • REQ: It is used to request a message pattern.
  • REP: It is used to response a message pattern.
  • PUB: It is used to publish a message pattern.
  • SUB: It is used to subscriber a message pattern.
  • PUSH: It is used to push a message pattern.
  • PULL: It is also used to push a message pattern.
  • PAIR: It is used to bidirectional communication between two peers.
  • ROUTER: It is used to routing messages between multiple peers.
  • DEALER: It is used to load balancing and routing messages between multiple peers.

Key Features

ZeroMQ has several key features including:

  • Asynchronous I/O: To handle multiple connections simultaneously ZeroMQ uses asynchronous I/O.
  • Message Queue: To handle messages that cannot be processed immediately ZeroMQ provides a message queue.
  • High Performance: Due to its high performance ZeroMQ can handle thousands of messages per second because.
  • Scalability: ZeroMQ is designed to scale horizontally so that it can handle a large number of connections.
  • Multi Transport: ZeroMQ supports multiple transport protocols including TCP, UDP, and in-process.
  • Multi Language: ZeroMQ has bindings for many languages including C, C++, Java, Python, etc.

Use Cases

We can use ZeroMQ in financial applications, distributed systems, or real-time data streams, irrespective of the field ZeroMQ offers performance and simplicity for even the complex messaging scenarios. Following is a list of the common usages of ZeroMQ −

  • Real-time System: ZeroMQ requires low latency and high throughput messaging so that it is used in real-time systems.
  • Distributed System: ZeroMQ requires communication between multiple nodes so that it is used in distributed systems.
  • Cloud Computing: To provide scalable and fault-tolerance messaging ZeroMQ is used in Cloud computing.
  • Big Data: To provide high-performance and scalable messaging ZeroMQ is used in Big Data processing.

ZeroMQ - Socket Types

In ZeroMQ, the Socket is a standard API (Application Programming Interface) for the network programming. That's why the ZeroMQ presents the familiar socket-based API's. One of the most interesting characteristics of ZeroMQ for developers is its use of different socket types to implement any messaging pattern.

Before discussing the socket types, it's important to first understand the concept of Messaging patterns. Understanding these patterns will provide clarity on how different sockets operate and are used in ZeroMQ.

Messaging Patterns

A Messaging pattern refers to the specific design and behavior of how messages are exchanged between different components of a distributed system using ZeroMQ sockets. ZeroMQ provides several built-in messaging patterns that satisfy various communication needs.

Following are the built-in core messaging patterns −

  • Request-reply Messaging Pattern
  • Pub-sub (Publish-subscribe) Messaging Pattern
  • Pipeline Messaging Pattern
  • Exclusive Pair Messaging Pattern

There are more ZeroMQ patterns that are still in draft state as follows −

  • Client-server Messaging Pattern
  • Radio-dish Messaging Pattern

Socket Types

The ZeroMQ messaging library provides several socket type that defines the semantics of the socket, including how it routes messages, and queues. Following is the list of messaging patterns and the sockets that belong to these patterns −

Socket types

Request-reply pattern

  • REQ Socket
  • REP Socket
  • Dealer Socket
  • Router Socket

Publish-subscribe pattern

  • PUB Socket
  • SUB Socket
  • XPUB Socket
  • XSUB Socket

Pipeline pattern

  • PUSH Socket
  • PULL Socket

Exclusive pair pattern

  • PAIR Socket

REQ Socket

The REQ is one of the initial socket types of the ZeroMQ messaging library comes under the synchronous flavors of the request-reply pattern. The request-reply pattern is designed for various kinds of service-oriented architectures.

The REQ socket is used by the client to send requests to and receive replies from a service. This type of socket allows only an altering sequence of sends and subsequent receive calls. It may connect to any number of REP or Router socket types..

Following is the summary of characteristics of the "REQ" Socket:

Characteristic Description
Compatible peer sockets REQ, DEALER
Direction Bidirectional
Send/receive pattern Receive, Send, Receive, Send ...
Outgoing routing strategy Fair-robin
Incoming routing strategy Last peer
Action in mute state Block

REP Socket

The REP socket is used by the services to receive requests from send replies to a client. This socket type allows only an alternating sequence ofreceiveand subsequentsendcalls. If the original requester does not exist anymore the reply is silently discarded.

Following is the summary of characteristics of the "REP" Socket:

Characteristic Description
Compatible peer sockets REQ, DEALER
Direction Bidirectional
Send/receive pattern Receive, Send, Receive, Send ...
Outgoing routing strategy Fair-robin
Incoming routing strategy Last peer

Dealer Socket

The DEALER socket type is a powerful and flexible messaging pattern used for building advanced communication patterns like request-reply, publish-subscribe, or other custom messaging schemes. It uses the "Round-Robin" algorithm to send and receive messages.

This socket works as an asynchronous replacement forREQ, for clients that communicate toREPorROUTERservers, and the messages received by aDEALERare fair-queued from all connected peers.

Following is the summary of characteristics of the "Dealer" Socket:

Characteristic Description
Compatible peer sockets ROUTER, REP, DEALER
Direction Bidirectional
Send/receive pattern Unrestricted
Outgoing routing strategy Round-robin
Incoming routing strategy Fair-queued
Action in mute state Block

Router Socket

A ROUTER socket is one of the socket types used for advanced message routing patterns. It enables complex routing logic by allowing you to address messages to specific clients. ROUTERworks as an asynchronous replacement forREP, and is often used as the basis for servers that communicate toDEALERclients.

It can send messages to specific peers using an address, which is often an identity string.

This socket supports message framing, which means it can handle multi-part messages. Each message sent through a ROUTER socket can be composed of multiple parts, where the first part is often used as a routing address, and the following parts contain the actual data.

Following is the summary of characteristics of the "Dealer" Socket:

Characteristic Description
Compatible peer sockets DEALER, REQ, ROUTER
Direction Bidirectional
Send/receive pattern Unrestricted
Outgoing routing strategy See text
Incoming routing strategy Fair-queued
Action in mute state Drop (see text)

PUB Socket

A PUB (publish) is one of the core socket types used by the publisher to distribute data referring to implementing the publish-subscribe messaging pattern. It is designed to send messages to multiple subscribers. This socket type is not able to receive any messages from the server.

Following is the summary of characteristics of the "PUB" Socket:

Characteristic Description
Compatible peer sockets SUB, XSUB
Direction Unidirectional
Send/receive pattern Send only
Outgoing routing strategy N/A
Incoming routing strategy Fan out
Action in mute state Drop

SUB Socket

A SUB (Subscribe) is one of the socket types used by the subscriber to subscribe to the data distributed by the publisher. Initially, aSUBsocket is not subscribed to any messages.

Following is the summary of characteristics of the "SUB" Socket:

Characteristic Description
Compatible peer sockets PUB, XPUB
Direction Unidirectional
Send/receive pattern Receive only
Outgoing routing strategy Fair-queued
Incoming routing strategy N/A

XPUB Socket

An XPUB (Extended Publish) socket is an advanced version of the PUB (Publish) socket used in the publish-subscribe messaging pattern. The XPUB socket provides additional features compared to a standard PUB socket.

Same asPUB, except that you can receive subscriptions from the peers in form of incoming messages. The subscription message is a byte 1 (for subscriptions) or byte 0 (for unsubscriptions) followed by the subscription body.

Following is the summary of characteristics of the "XPUB" Socket:

Characteristic Description
Compatible peer sockets ZMQ_SUB, ZMQ_XSUB
Direction Unidirectional
Send/receive pattern Send messages, receive subscriptions
Outgoing routing strategy N/A
Incoming routing strategy Fan out
Action in mute state Drop

XSUB Socket

An XSUB (Extended Subscribe) socket is an advanced version of the SUB (Subscribe) socket used in the publish-subscribe messaging pattern. The XSUB socket provides additional features compared to a standard SUB socket.

Same asSUBexcept that you subscribe by sending subscription messages to the socket. The subscription message is a byte 1 (for subscriptions) or byte 0 (for non-subscriptions) followed by the subscription body.

Following is the summary of characteristics of the "XSUB" Socket:

Characteristic Description
Compatible peer sockets ZMQ_PUB, ZMQ_XPUB
Direction Unidirectional
Send/receive pattern Receive messages, send subscriptions
Outgoing routing strategy Fair-queued
Incoming routing strategy N/A
Action in mute state Drop

PUSH Socket

APUSH socket is a type of socket that is designed for sending messages to one or more PULL sockets using a "round-robin" algorithm in a simple and scalable manner. Each connected PULL socket receives messages from the PUSH socket in a round-robin technique, ensuring that the workload is balanced among multiple receivers.

Following is the summary of characteristics of the "PUSH" Socket:

Characteristic Description
Compatible peer sockets PULL
Direction Unidirectional
Send/receive pattern Send only
Outgoing routing strategy N/A
Incoming routing strategy Round-robin
Action in mute state Block

PULL Socket

APULLsocket is a type of socket that is designed for receiving messages using a "fair-queuing" algorithm in a simple and scalable manner. It is often used in conjunction with a PUSH socket, which sends messages to one or more PULL sockets.

Following is the summary of characteristics of the "PULL" Socket:

Characteristic Description
Compatible peer sockets PUSH
Direction Unidirectional
Send/receive pattern Receive only
Outgoing routing strategy Fair-queued
Incoming routing strategy N/A
Action in mute state Block

PAIR Socket

A PAIR socket is a type of socket that belongs to the "Exclusive pair pattern". It is designed for a simple, one-to-one communication pattern and allows for a direct connection between two peers, providing a way for them to send messages back and onwards.

ZeroMQ - Communication Patterns

ZeroMQ is a high-performance, asynchronous messaging library that provides a set of communication patterns for generating distributed applications. It provides a simple and structured way to implement messaging patterns in various applications. Here are some basic core concepts of ZeroMQ −

  • Sockets as Communication Endpoints: ZeroMQ sockets support advanced messaging patterns such as PUB-SUB and PUSH-PULL.
  • Asynchronous Message Passing: ZeroMQ socket operates asynchronously, allowing applications to send and receive message without blocking.
  • Scalability & Performance: ZeroMQ is designed to scale horizontally, allowing many-to-many configurations while maintaining high throughput and low latency.

Communication Patterns

The following is a list of communication patterns:

  • Request-Reply (REQ-REP)
  • Publish-Subscribe (PUB-SUB)
  • Push-Pull (PUSH-PULL)
  • Pair (PAIR)
  • Dealer-Router (DEALER-ROUTER)
  • Router-Dealer (ROUTER-DEALER)
  • XPUB-XSUB

These communication patterns can be combined in various ways to create complicated distributed systems. ZeroMQ provides a flexible and scalable messaging framework that allows developers to implement anything from simple client-server configurations to complex distributed architectures.

Request-Reply

This is a remote service call and task distribution communication pattern that connects a group of clients to a group of services. This means that it is a simple client-server communication where the client sends a request to the server, and the server responds with a reply.

For example, consider a web server that responds to HTTP requests from clients.

Req-Rep-Pattern

Example

Following is an example of REQ/REP. First, we connect to the server and get responses from clients.

package com.zeromq.mavenProject;

import org.zeromq.SocketType;
import org.zeromq.ZMQ;
import org.zeromq.ZContext;

public class Practice {
   public static void main(String[] args) {
      try (ZContext context = new ZContext()) {
         System.out.println("Connecting to TP server");

         //  Socket to talk to server
         ZMQ.Socket socket = context.createSocket(SocketType.REQ);
         socket.connect("tcp://localhost:5555");

         for (int requestNbr = 0; requestNbr != 5; requestNbr++) {
             String request = "Tutorialspoint";
             System.out.println("Sending TP " + requestNbr);
             socket.send(request.getBytes(ZMQ.CHARSET), 0);

             byte[] reply = socket.recv(0);
             System.out.println(
                "Received " + new String(reply, ZMQ.CHARSET) + " " +
                requestNbr
             );
         }
      }
   }
}

Output

Connecting to TP server
Sending TP 0
Received world 0
Sending TP 1
Received world 1
Sending TP 2
Received world 2
Sending TP 3
Received world 3
Sending TP 4
Received world 4

Publish-Subscribe

This is a data distribution communication pattern that links a group of publishers to a group of subscribers. It is used to broadcast messages to multiple recipients.

For example, consider a news feed system that broadcasts news articles to multiple subscribers.

Pub-Sub-Pattern

Example

In this example, the publisher creates a PUB socket, binds to a port, and sends messages. A subscriber creates a SUB socket, connects to the publisher, receives messages, prints out those received messages.

// Publisher (PUB) class
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;

// After running the PUB comment it runs SUB.

public class PubSubPublisher {
   public static void main(String[] args) {
      Context context = ZMQ.context(1);
      Socket socket = context.socket(ZMQ.PUB);
      socket.bind("tcp://*:5555");

      while (true) {
         socket.send("Hello, subscribers!".getBytes(), 0);
      }
   }
}

// Subscriber (SUB) class
public class PubSubSubscriber {
   public static void main(String[] args) {
      Context context = ZMQ.context(1);
      Socket socket = context.socket(ZMQ.SUB);
      socket.connect("tcp://localhost:5555");
      socket.subscribe("".getBytes()); // Subscribe to all messages

      while (true) {
         byte[] message = socket.recv(0);
         System.out.println("Received message: " + new String(message));
      }
   }
}

Output

Received message: Hello, subscribers!
Received message: Hello, subscribers!
Received message: Hello, subscribers!
Received message: Hello, subscribers!
Received message: Hello, subscribers!
Received message: Hello, subscribers!

Push-Pull

This pattern is useful for load balancing and task distribution. The push socket sends messages to the pull socket, which receives and then processes the messages.

For example, consider a load balancer that distributes tasks to multiple worker nodes.

Push-Pull-Pattern

Example

In this example, the pusher creates a PUSH socket and connects to the puller. The puller creates a pull socket, binds it to the port, receives work items from the pusher, and prints them.

package com.zeromq.mavenProject;

import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;

// After running the PUSH comment it runs PULL.

public class PushPullPusher {
   public static void main(String[] args) {
      // Create a PUSH socket and connect to the puller
      Context context = ZMQ.context(1);
      Socket socket = context.socket(ZMQ.PUSH);
      socket.connect("tcp://localhost:5500");

      // Send work items to the puller
      while (true) {
         socket.send("Work item".getBytes(), 0);
      }
   }
}

public class PushPullPuller {
   public static void main(String[] args) {
      // Create a PULL socket and bind it to a port
      Context context = ZMQ.context(1);
      Socket socket = context.socket(ZMQ.PULL);
      socket.bind("tcp://*:5500");

      // Receive work items from the pusher
      while (true) {
         byte[] message = socket.recv(0);
         System.out.println("Received work item: " + new String(message));
      }
   }
}

Output


Pair

This pattern can be used in simple peer-to-peer interactions. A pair of sockets are connected with each other in a peer-to-peer fashion so that two nodes can communicate bidirectionally.

For example, consider a chat application that allows two users to communicate each-other.

Pair-Pattern

Example

In this example, we demonstrate the PAIR pattern by creating two nodes that can send and receive messages to each other. We are checking whether the connection is bidirectional or asynchronous. If yes, each node waits for the other node to respond before sending the next message.

package com.zeromq.mavenProject;

import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;

// After running the PairNode1 comment it runs PairNode2.

public class PairNode1 {
   public static void main(String[] args) {
      // Create a PAIR socket and bind it to a port
      Context context = ZMQ.context(1);
      Socket socket = context.socket(ZMQ.PAIR);
      socket.bind("tcp://*:5000");

      // Receive messages from Node 2 and send responses
      while (true) {
         byte[] message = socket.recv(0);
         System.out.println("Received message: " + new String(message));
         socket.send("Hello, Node 2!".getBytes(), 0);
      }
   }
}

public class PairNode2 {
   public static void main(String[] args) {
      // Create a PAIR socket and connect to Node 1
      Context context = ZMQ.context(1);
      Socket socket = context.socket(ZMQ.PAIR);
      socket.connect("tcp://localhost:5000");

      // Send messages to Node 1 and receive responses
      while (true) {
         socket.send("Hello, Node 1!".getBytes(), 0);
         byte[] message = socket.recv(0);
         System.out.println("Received response: " + new String(message));
      }
   }
}

Output

Received message: Hello, Node 1!
Received message: Hello, Node 1!
Received message: Hello, Node 1!
Received message: Hello, Node 1!
Received message: Hello, Node 1!

Dealer-Router

This pattern used to implement complex distributed systems. A dealer socket sends messages to a router socket, which dispatches them to one or more connected peers.

For example, consider a distributed database system that routes queries to multiple nodes.

Router-Dealer

This pattern helps in implementing complex distributed systems. A router socket routes messages from one or more connected dealer sockets, each sending it to its respective peer.

For example, consider a content delivery network that routes to multiple edge servers.

XPUB-XSUB

This communication pattern is useful in creating dynamic publish-subscribe systems. XPUB is a special publisher that allows subscribers to connect and disconnect dynamically. XSUB is a special subscriber, which can connect to multiple publishers.

For example, consider a real-time analytics system where multiple publishers send data to multiple subscribers.

ZeroMQ - Transport Protocols

The transport protocol in ZeroMQ is known as the ZeroMQ Message Transport Protocol (ZMTP). It is a transport layer protocol for exchanging messages between two hosts over a connected transport layer such as Transmission Control Protocol (TCP).

TCP is the main protocol in the Internet protocol suite, often referred to as TCP/IP. TCP allows us to use two hosts or devices to connect and exchange data streams.
transport protocol

Here are some of the key features of ZMTP:

  • Security: ZMTP supports extensible security mechanisms. Peers agree on the security mechanism and version by exchanging data and persisting or closing the connection.
  • Backward Interoperability: ZMTP defines rules for backward interoperability.
  • Command and Messaging Framing: ZMTP defines rules for command and message framing.
  • Connection Metadata: ZMTP defines rules for connection metadata.

ZeroMQ supports several transport protocols that manage the exchange of messages between endpoints. Here are the primary transport protocols used in ZeroMQ:

TCP (Transmission Control Protocol)

It is the most reliable connection-oriented protocol that ensures that all messages are delivered in the correct order without duplication. This is typically used when processes are communicating over a network, whether on the same machine or on different machines.

Bind Example (Server):

// Bind to all available network interfaces on port 5555
socket.bind("tcp://*:5555")

Connect Example (Client):

// Connect to a remote server on the specified IP and port
socket.connect("tcp://192.168.0.1:5555")

Advantages

Following are the advantages of using TCP in ZeroMQ −

  • It is widely supported across all platforms and devices.
  • It has reliable message delivery.
  • It ensures that messages are received in order.

Disadvantages

Following are the disadvantages of using TCP in ZeroMQ −

  • It has higher latency compared to other protocols like Inter-Process Communication (IPC).
  • It also has more overhead from error checking, flow control, and acknowledgement packets.

IPC (Inter Process-Communication)

IPC allows communication between different processes on the same machine. It is better than TCP because it avoids network stack overhead by operating directly at the operating system level.

Bind Example (Server):

// Bind to a local file-based endpoint
socket.bind("ipc:///tmp/zeromq-ipc")

Connect Example (Client):

// Connect to a local endpoint on the same machine
socket.connect("ipc:///tmp/zeromq-ipc")

Advantages

Following are the advantages of using IPC in ZeroMQ −

  • It is much faster than network-based protocols like TCP.
  • There is no network latency since it works on the same machine.
  • It is ideal for communication between different processes in the same application or microservices on the same server.

Disadvantages

Following are the disadvantages of using IPC in ZeroMQ −

  • It is limited to communication between processes on the same machine.
  • It cannot be used for communication between different machines

Inproc (In-Process)

Inproc is used for communication between threads within the same process. It is the fastest ZeroMQ transport because it avoids interprocess communication, relying only on memory transfers within the same application.

Bind Example (Thread 1):

// Bind to an in-process endpoint
socket.bind("inproc://some-endpoint")

Connect Example (Thread 2):

// Connect to the in-process endpoint from another thread
socket.connect("inproc://some-endpoint")

Advantages

Following are the advantages of using Inproc in ZeroMQ −

  • It is the fastest transport as it does not involve any networking or process limitations.
  • It supports communication between threads within the same application.

Disadvantages

Following are the disadvantages of using Inproc in ZeroMQ −

  • It is not suitable for communication between different processes or across a network.
  • It is limited to inter-thread communication within a single process.

PGM/EPGM

The PGM/EPGM (Pragmatic General Multicast/Encapsulated Pragmatic General Multicast) is used for applications that require reliable multicast delivery, such as real-time data delivery or live streaming. These protocols enable multicast communications, where a sender sends messages to multiple receivers simultaneously.

  • PGM: This is native multicast and requires network layer support. It is not available on all network infrastructures.

  • EPGM: ZeroMQ wraps PGM over UDP (encapsulation), allowing multicast to work on networks that do not directly support PGM.

Bind Example (Server):

// Bind to a multicast address and port
socket.bind("epgm://eth0;239.192.1.1:5555")

Connect Example (Client):

// Connect to the multi-cast group
socket.connect("epgm://239.192.1.1:5555")

Advantages

Following are the advantages of using PGM/EPGM in ZeroMQ −

  • It is efficient for scenarios where one message needs to reach multiple clients (e.g., stock ticker updates, live sports scores).
  • It minimizes network bandwidth by sending one message to multiple subscribers.

Disadvantages

Following are the disadvantages of using PGM/EPGM in ZeroMQ −

  • It is more complex than unicast-based protocols like TCP.
  • PGM is not widely supported and may require special network configuration.

UDP (User Datagram Protocol)

UDP is a connectionless and unreliable protocol that can be faster than TCP but does not guarantee message delivery or ordering. While, ZeroMQ does not directly support raw UDP as a transport (unlike TCP), it can be used indirectly via PGM or other protocols.

TIPC (transparent Inter Process-Communication)

TIPC is a transport protocol designed for efficient communication between nodes in a cluster. It is present in the Linux operating system and is optimized for high-speed inter-process communication within a cluster of machines.

Bind Example (Server):

// Bind to a TIPC service and instance
socket.bind("tipc://{service,instance}")

Connect Example (Client):

// Connect to a TIPC service on the cluster
socket.connect("tipc://{service,instance}")

Advantages

UDP is efficient for communication and high-speed transport design in cluster environments, especially for Linux clusters.

Disadvantages

Following are the disadvantages of using UDP in ZeroMQ −

  • It is limited to Linux environments with TIPC support.
  • It may require additional network setup to be used effectively.

In summary, each protocol has its strengths and weaknesses, and your choice will depend on the architecture of your system, performance requirements, and the environment in which your applications process.

ZeroMQ - Message Framing

Understanding Messages in ZeroMQ

A Message in ZeroMQ is a discrete data unit that is passed between applications or components of the same application. From ZeroMQ's point of view, messages are considered opaque binary data.

They are blocks of data sent and received over the network using various message patterns supported by ZeroMQ. ZeroMQ provides several message patterns as follows −

  • PUB/SUB (Publish/Subscribe)
  • REQ/REP (Request/Reply)
  • PUSH/PULL
  • Router/Dealer

Message Framing

The ZeroMQ messaging library refers to the structure and organization of messages exchanged between applications or services. The messaging framework provides a way to construct, send, and receive messages, and ensuring that messages are properly formatted, routed, and processed by the receiving application.

Here is a simple diagram to demonstrates how ZeroMQ frames messages −

Message framing

Here,

  • Socket: The Socket handles communication over the network. It sends and receives messages and manages the framing internally.
  • Queue: It represents the internal mechanism that ZeroMQ uses to store and manage messages and their frames. It ensures that messages are processed correctly, even if split into multiple frames.
  • Message: It is a logical unit of data composed of one or more frames.
  • Frame: The individual data chunks that make up a message. Frames are sent and received in order, and reassembled into complete messages by ZeroMQ.

How Messages are Managed and Structured?

In ZeroMQ, the concepts of Envelope, Body, and Frames are important for understanding how messages are structured and managed:

  • Envelope
  • Body
  • Frames

Envelope

In ZeroMQ, the term envelope is often used in the context of the ROUTER socket. An envelope generally includes metadata such as routing information, which routes messages to the correct receiver. It is more commonly associated with ROUTER sockets, where the envelope consists of the message routing identity and other metadata.

Body

The body of the message in ZeroMQ refers to the actual data being sent. This is the payload of the message that the application processes. The body is the main content being transmitted between sockets.

Frames

The concept of frames is relevant when dealing with more complex socket types like STREAM and ROUTER. Messages can be split into multiple frames, where each frame is a chunk of data that together forms the complete message. This is useful for handling large messages or messages with multiple parts.

ZeroMQ itself does not define or implement specific types of message framing as some protocols do. Instead, ZeroMQ provides a flexible messaging system where the framing and serialization of messages are handled by the application. However, it provides several messaging patterns and socket types that implicitly handle message framing in different ways. They are −

Publish/Subscribe (PUB/SUB)

  • Publisher (PUB) - Sends messages to multiple subscribers.
  • Subscriber (SUB - Receives messages based on subscription filters.
  • Framing - ZeroMQ handles the framing internally, but messages are published as they are.

Request/Reply (REQ/REP)

  • Request (REQ) - Sends a request to a reply socket and waits for a response.
  • Reply (REP) - Receives requests and sends responses.
  • Framing - ZeroMQ frames messages as discrete entities, maintaining the request and response protocol.

DEALER and ROUTER

  • Dealer (DEALER) - Acts as a non-blocking requester, and able to handle multiple requests simultaneously.
  • Router (ROUTER) - Handles messages in a more complex way, routing them based on identity or other criteria.
  • Framing - Messages include routing metadata or identifiers, and ZeroMQ frames messages with this metadata for routing.

PUSH and PULL

  • Push (PUSH) - Sends messages to a pull socket, generally used for distributing tasks.
  • Pull (PULL) - Receives messages from a push socket.
  • Framing - Messages are distributed in a round-robin technique, with ZeroMQ handling the framing internally.

Delivery Guarantees

ZeroMQ does not provide a built-in guarantee of message delivery, and there is no automatic retry mechanism in ZeroMQ. If a message is sent and the receiver is not available or there is a network failure, the message may be lost.

ZeroMQ - Synchronous Message Processing

The ZeroMQ is a high-performance asynchronous universal messaging library used for building distributed or concurrent applications. It is also known as MQ. As we know that ZeroMQ is primarily designed for asynchronous message passing, you can also implement synchronous message processing patterns using various socket types and messaging patterns.

Key Concepts

The following are important topics that you should know while performing synchronous message processing in ZeroMQ −

  • Socket: In ZeroMQ, a socket is an endpoint (API) for sending or receiving data between client and server. It represents a communication channel between two components (such as processes or devices) that allows them to exchange data. ZeroMQ provides different types of sockets for various messaging patterns such as PUB/SUB, REQ/REP, PUSH/PULL, etc.

  • Messaging Queuing: The ZeroMQ sockets are used to send and receive messages. They dont require a dedicated message broker, as the messaging patterns are implemented in the library itself, which makes it "broker less".

  • Asynchronous Nature: The ZeroMQ is primarily designed as asynchronous, which means it is designed to handle multiple operations at once, which is why it is often used for high-performance, non-blocking communication.

Here is the diagram of Synchronous Message Processing −

Synchronous Message Processing

Implementing Synchronous Processing

The synchronous process is a type of operation where tasks or operations are executed sequentially, with each step or action waiting for the previous one to complete before proceeding.

To achieve synchronous processing with ZeroMQ, which follows an asynchronous mechanism by default, you can use specific patterns and socket types. The most common approach for synchronous message processing is using the REQ (request) and REP (reply) sockets, which follow a request-reply pattern. In the REQ/REP request pattern −

  • REQ Socket: It is used by client to send request or data to the server.
  • REP Socket: It is used by server to respond to received request.

Synchronous Message Processing in Java

Here is a basic example of synchronous message processing using ZeroMQ in Java

Server code (REP Socket)

import org.zeromq.ZContext;
import org.zeromq.SocketType;
import org.zeromq.ZMQ;
import org.zeromq.ZContext;

public class Response {
   public static void main(String[] args) {
      try (ZContext context = new ZContext()) {
         ZMQ.Socket socket = context.createSocket(ZMQ.REP);
         socket.bind("tcp://127.0.0.1:5555");

         while (true) {
            // Wait for the next request from the client
            String message = socket.recvStr();
            System.out.println("Received request: " + message);

            // Send a reply back to the client
            socket.send("Welcome, " + message);
         }
      }
   }
}

Client code (REQ Socket)

import org.zeromq.ZContext;
import org.zeromq.SocketType;
import org.zeromq.ZMQ;
import org.zeromq.ZContext;

public class Request {
   public static void main(String[] args) {
      ZMQ.Context context = ZMQ.context(1);
      ZMQ.Socket socket = context.socket(ZMQ.REQ);
      socket.connect("tcp://127.0.0.1:5555");

      // Send a request
      socket.send("to World".getBytes(ZMQ.CHARSET), 0);

      // Receive the reply
      byte[] reply = socket.recv(0);
      System.out.println("Received reply: "+new String(reply,ZMQ.CHARSET));

      socket.close();
      context.term();
   }
}

Output

After executing the above programs, the following output will be displayed −

Received reply: Welcome, to World

Synchronous Message Processing in Python

Here is a basic example of synchronous message processing using ZeroMQ in Python

Server code (REP Socket)

import zmq
def main():
   context = zmq.Context()
   socket = context.socket(zmq.REP)
   socket.bind("tcp://127.0.0.1:5555")

   while True:
      # Wait for the next request from the client
      message = socket.recv_string()
      print(f"Received request: {message}")

      # Send a reply back to the client
      socket.send_string(f"Welcome, {message}")

if __name__ == "__main__":
   main()

Client code (REQ Socket)

import zmq
def main():
   context = zmq.Context()
   socket = context.socket(zmq.REQ)
   socket.connect("tcp://127.0.0.1:5555")

   # Send a request
   socket.send_string("to World")

   # Receive the reply
   message = socket.recv_string()
   print(f"Received reply: {message}")

if __name__ == "__main__":
   main()

Output

The above programs produces the following output −

Received reply: Welcome, to World

ZeroMQ - Multithreading

A multi-threading is a technique that allows to perform multiple tasks at a time within the same application, and it can be used with ZeroMQ to handle tasks over multiple threads simultaneously.

In the previous chapters, we had already discussed that ZeroMQ is a high-performance messaging library that provides several messaging patterns, such as publish-subscribe, request-reply, and more, to completely use ZeroMQ in a multi-threaded environment, you must consider several important characteristics.

Thread Safety

It is designed to be thread-safe, which means you can use ZeroMQ sockets in multiple threads without running into data corruption or race condition problems. It is important to ensure that the application code that interacts with ZeroMQ follows thread safety principles.

Socket access

The sockets in ZeroMQ are used to manage and enable communication between different parts of a distributed application. As they are thread-safe, so it is recommended to avoid sharing sockets between threads if not necessary. Instead, each thread should work with its own set of sockets to prevent potential problems and simplify code maintenance.

Context Management

The context in ZeroMQ is used to manage the state of the library and is created once in each application. The context is thread-safe, so you can share it between threads. However, you should make sure that you do not create multiple contexts unnecessarily, as a single context can manage all the sockets efficiently.

Message Handling

When working with message processing in a multi-threaded environment, make sure the message handling logic is develop to handle concurrent access. This is usually involves using synchronization primitives such as mutexes or locks where necessary.

Concurrency Pattern

ZeroMQ provides various concurrency patterns to facilitate scalable and efficient message processing in distributed systems. Depending on your application needs, you can choose different concurrency patterns such as producer-consumer, work-stealing, etc. The ZeroMQ messaging library allows you to implement these patterns in a multi-threaded context.

Diagram of Multi-threading

The following diagram illustrates multi-threading in ZeroMQ, it shows how multiple threads interact with ZeroMQ sockets to manage concurrent communication. This can be useful for understanding how different threads can handle messaging tasks simultaneously −

Multithreading

In the above diagram you can observe two layers namely −

  • Application Layer: It represents the main application that managing multiple threads and ZeroMQ sockets.
  • Threads: You can observe three threads (Thread 1, Thread 2, Thread 3).

Where −

  • Thread 1: The thread1 handles a PUB (Publisher) socket and a REP (Replier) socket.
  • Thread 2: The thread2 manages a SUB (Subscriber) socket and a PUSH (Pusher) socket.
  • Thread 3: The thread3 operates a PULL (Puller) socket and a DEALER (Dealer) socket.

ZeroMQ - Performance Considerations

In general, performance considerations are as important as the success of applications and cloud-based solutions.

There are several points that we should keep in mind to optimize the performance of ZeroMQ (ZMQ). ZeroMQ is considered a high-performance, asynchronous messaging library, but we must consider both hardware and software factors to make the most use of it.

The following are the number of factors that affect the ZeroMQPerformance −

  • Memory Usage
  • Latency
  • Throughput
  • Stack

Let's discuss the above factors in detail −

Memory Usage

ZeroMQ is considered to be used as thin as possible, so that it can run even in a platforms having limited memory. The low memory footprint is also important to optimize the use of the L1i cache. When the code size is small enough the processor can keep the entire messaging code in the L1i cache and avoid slow accesses to physical memory to get new parts of the code.

The part of the library holding the actual code is 80kB long and the shared library is about 350kB on Linux platforms. Still, a lot of the code is inline, so it is placed as inline functions in header files rather than in libraries.

The following tables shows the memory usage as reported by top utility −

Application Virtual Memory Resident Memory Resident Code
Sender 24312kB 1360kB 12kB
Receiver 24308kB 1360kB 8kB

Latency

Latency indicates the time delay between a cause and its effect, usually in a communication system or process. In the context of computing and networking, latency measures the time it takes for data to travel from one point to another or for a system to respond to an action.

We have latency measured of 40 microseconds for 1-byte messages, with the networking stack taking 25 microseconds and ZeroMQ taking 15 microseconds. Using 10GbE at a TCP latency of 15 microseconds, we would expect latency to be around 30 microseconds. We also have a solution in which we bypass the OS kernel to talk directly to the hardware which aims to reduce the latency below 20 microseconds.

Low latency: If low latency is very important, the ZMQ_IMMEDIATE socket option should be used to prevent any buffering. It is also advisable to use in-proc (in memory messaging between thread) or inter process communication sockets instead of TCP since they are less costly.

Throughput

Throughput is nothing but the rate at which at which ZeroMQ sends and receives messages over a network. This is measured in message per second (mps) or even data volume per second (mb/s).

Throughput is affected by the following factors, including message size, socket types, network bandwidth, and system performance.

  • Message Size: The larger message reduce the rate of message sent per second, while smaller message increase it.
  • Socket type: The socket types push-pull or pub-sub can impact how message are distributed and how fast they are processed.
  • Batching: Sending message in batches reduces overhead and increase the throughput.
  • Network Bandwidth: The higher bandwidth allows more data to flow through, improving the throughput.
  • System Resources: CPU, memory, and thread utilization affect how efficiently messages are processed.

Stack

In ZeroMQ, the stack refers to the layers of software and components required to send and receive messages. The entire stack, including network devices, NICs, processors, and operating systems, can affect results.It is generally composed of −

Application Layer

It is a layer where our application code interacts with ZeroMQ using its API. We define how messages are sent and received between different sockets (PUB-SUB, PUSH-PULL, etc.).

Messaging Layer

This layer manages message queuing, dispatch and routing based on the socket pattern we choose such as REQ-REP, PUB-SUB. It handles message buffering, batching, and flow control.

Transport Layer

This layer decides how to deliver a message depending on the chosen transport protocols. The following are the transport protocols:

  • inproc: It is an in-process communication (threads within the same process).
  • ipc: It is an inter-process communication (process on the same machine).
  • tcp: It is used to communicate over a TCP network.
  • pgm/epgm: It is a multicast transport protocol for broadcast.

Network Stack

ZeroMQ uses a system's networking layer like TCP/IP, which handles lower-level tasks such as packet transmission, retransmission in the case of TCP, and congestion control.

Hardware

The physical hardware, including the network interface, drives the actual transmission and receipt of the message.

Advertisements