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.

Advertisements