Good morning. In this post we will explore RabbitMQ by Pivotal. RabbitMQ is a message broker. Message brokers are considered middleware. Applications subscribe to a queue to send and receive messages. Another queue broker I have used in products is MSMQ from Microsoft. In this post we will only experiment with RabbitMQ. You may decide to use message queues to communicate between microservices.
For this post I decided to use Windows 10. With some minor changes regarding installation, the software written in Java should also work without modifications in Linux.
RabbitMQ is a message broker: it accepts and forwards messages. You can think about it as a post office: when you put the mail that you want posting in a post box, you can be sure that the postal carrier will eventually deliver the mail to your recipient. In this analogy, RabbitMQ is a post box, a post office and a postman.
Producing means nothing more than sending. A program that sends messages is a producer.
A queue is the name for a post box which lives inside RabbitMQ. Although messages flow through RabbitMQ and your applications, they can only be stored inside a queue. A queue is only bound by the host’s memory & disk limits, it is essentially a large message buffer. Many producers can send messages that go to one queue, and many consumers can try to receive data from one queue.
Consuming has a similar meaning to receiving. A consumer is a program that mostly waits to receive messages.
Note that the producer, consumer, and broker do not have to reside on the same host; indeed in most applications they do not. An application can be both a producer and consumer.
RabbitMQ speaks multiple protocols. In this example we will use AMQP 0-9-1, which is an open, general-purpose protocol for messaging. There are a number of clients for RabbitMQ in many different languages. We will use the Java client provided by RabbitMQ.
The code in this post is the equivalent Hello World for many languages and products. We will write two simple applications. One will send a message using a queue. The second will read from the same queue the message.
Before writing any Java code we need to install the prerequisites for RabbitMQ. In my case I am running on a 64-bit machine. We need to download and install Erlang. Erlang is a general purpose, concurrent, functional programming language. To learn more about it you may read this post in Wikipedia.
I downloaded and installed Erlang in my computer using all the provided defaults. I installed it using the following information:
URL | http://www.erlang.org/downloads |
Download | C:\temp\otp_win64_21.2.exe |
Location | C:\Program Files\erl10.2 |
After installing Erlang I restarted my machine.
To download and install RabbitMQ I used all the defaults provided by the installer and the following information:
URL | https://www.rabbitmq.com/install-windows.html |
Download | c:\temp\rabbitmq-server-3.7.12.exe |
Location | C:\Program Files\RabbitMQ Server |
After installing RabbitMQ I also restarted my computer.
Let’s take a look at the console output of the receiver:
C:\Temp>java -jar rabitt.jar recv args.length: 1 s ==>recv<== [*] Waiting for messages. To exit press CTRL+C [x] Received ==>Hello World !!!<== [x] Received ==>Hello World !!!<== [x] Received ==>Hello World !!!<== Terminate batch job (Y/N)? y C:\Temp>
Note that we invoke a batch file. The same message “Hello World !!!” is received three times. We enter a ctrl-C and we are prompted to exit.
The associated sender output running on a different console follows:
C:\Temp>rabbit-send.bat C:\Temp>java -jar rabitt.jar send args.length: 1 s ==>send<== [x] Sent ==>Hello World !!!<== C:\Temp> C:\Temp>rabbit-send.bat C:\Temp>java -jar rabitt.jar send args.length: 1 s ==>send<== [x] Sent ==>Hello World !!!<== C:\Temp>rabbit-send.bat C:\Temp>java -jar rabitt.jar send args.length: 1 s ==>send<== [x] Sent ==>Hello World !!!<== C:\Temp>
This is also a batch file. The batch file is invoked three times. Each time we send the same string “Hello World !!!”.
There are different ways to run a java JAR file on Windows. In this case we encapsulate the JAR we generate for both sender and receiver in their separate batch files. The batch file for the receiver follows:
java -jar rabbit.jar recv
The batch file for the sender follows:
java -jar rabbit.jar send
In the original source code I made a typo and used “rabitt” instead or “rabbit”. I believe all typos were addressed.
The code for the solution follows:
package com.canessa.rabbit; public class Solution { public static void main(String[] args) throws Exception { // **** **** System.out.println("args.length: " + args.length); // **** **** for (String s : args) { // **** **** System.out.println("s ==>" + s + "<=="); // **** **** if (s.equals("send")) { Send send = new Send(); send.send(); } else if (s.equals("recv")) { Recv recv = new Recv(); recv.recv(); } else { System.out.println("unexpected s ==>" + s + "<=="); } } } }
In the main() method we pick up on the argument and decide to call the sender or the receiver.
The code for the receiver follows:
package com.canessa.rabbit; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.DeliverCallback; public class Recv { private final static String QUEUE_NAME = "hello"; public void recv() throws Exception { // **** **** ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); // **** **** Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); // **** **** System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); // **** **** DeliverCallback deliverCallback = (consumerTag, delivery) -> { String message = new String(delivery.getBody(), "UTF-8"); // **** **** System.out.println(" [x] Received ==>" + message + "<=="); }; channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { }); } }
We specify our computer and a queue named “hello”. We then wait for and display messages until we press control-C.
The code for the sender follows:
package com.canessa.rabbit; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; public class Send { private final static String QUEUE_NAME = "hello"; public void send() throws Exception { // **** **** ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); // **** **** try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) { // **** **** channel.queueDeclare(QUEUE_NAME, false, false, false, null); String message = "Hello World !!!"; channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8")); // **** **** System.out.println(" [x] Sent ==>" + message + "<=="); } } }
In a similar fashion to the receiver, we also connect to the RabbitMQ server running on our Windows machine and use the “hello” queue. Please note that the RabbitMQ server must be up and running in order for the code to work. You may check this out using the following:
C:\Temp>net start These Windows services are started: Adobe Acrobat Update Service Application Host Helper Service Application Information Background Tasks Infrastructure Service Base Filtering Engine Broadcom Management Agent Capability Access Manager Service Certificate Propagation Client License Service (ClipSVC) CNG Key Isolation COM+ Event System Computer Browser Connected Devices Platform Service Connected Devices Platform User Service_32aaf1 Connected User Experiences and Telemetry CoreMessaging Credential Manager Cryptographic Services Data Usage DCOM Server Process Launcher Delivery Optimization Dell System Manager Service Device Association Service DHCP Client Diagnostic Policy Service Diagnostic Service Host Distributed Link Tracking Client DNS Client Extensible Authentication Protocol Function Discovery Resource Publication Geolocation Service IIS Admin Service IP Helper IPsec Policy Agent Local Session Manager Message Queuing MongoDB MySQL56 Net.Msmq Listener Adapter Net.Pipe Listener Adapter Net.Tcp Listener Adapter Net.Tcp Port Sharing Service Network Connected Devices Auto-Setup Network Connection Broker Network List Service Network Location Awareness Network Store Interface Service NVIDIA Display Driver Service NVIDIA WMI Provider Offline Files Payments and NFC/SE Manager PDF Architect 3 Creator Plug and Play Power Print Spooler Program Compatibility Assistant Service Pulse Secure Network Service RabbitMQ <=== shows the service is running Remote Access Connection Manager Remote Desktop Configuration Remote Desktop Services Remote Desktop Services UserMode Port Redirector Remote Procedure Call (RPC) RPC Endpoint Mapper Secure Socket Tunneling Protocol Service Security Accounts Manager Security Center Sentinel LDK License Manager Server Shell Hardware Detection ShrewSoft IKE Daemon ShrewSoft IPSEC Daemon Software Protection SQL Server (MSSQLSERVER) SQL Server Agent (MSSQLSERVER) SQL Server Browser SQL Server VSS Writer SSDP Discovery State Repository Service Storage Service Superfetch Sync Host_32aaf1 System Event Notification Service System Events Broker System Guard Runtime Monitor Broker Task Scheduler TCP/IP NetBIOS Helper Themes Time Broker Touch Keyboard and Handwriting Panel Service Update Orchestrator Service User Manager User Profile Service WD Backup WD Drive Manager Web Account Manager Windows Audio Windows Audio Endpoint Builder Windows Connect Now - Config Registrar Windows Connection Manager Windows Defender Antivirus Network Inspection Service Windows Defender Antivirus Service Windows Defender Firewall Windows Defender Security Center Service Windows Event Log Windows Font Cache Service Windows License Manager Service Windows Management Instrumentation Windows Phone IP over USB Transport (IpOverUsbSvc) Windows Process Activation Service Windows Push Notifications System Service Windows Push Notifications User Service_32aaf1 Windows Remediation Service Windows Search Windows Time WinHTTP Web Proxy Auto-Discovery Service Wired AutoConfig Workstation World Wide Web Publishing Service Xamarin Bonjour Service The command completed successfully.
The code for the pom.xml file follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>RabbitMQHello</groupId> <artifactId>RabbitMQHello</artifactId> <version>0.0.1-SNAPSHOT</version> <build> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.5.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> </dependency> </dependencies> </project>
The POM shows the two dependencies I used for the client and logging.
As I mentioned earlier, in a future post, I will be using RabbitMQ on a Linux platform to get some microservices talking to each other. Also, the code for this project is in my GitHub repository.
If you have comments or questions regarding this or any other post in this blog, or if you would like some help on a software development project, please feel free to contact me by leaving a message below. Your request will not be made public.
Keep on learning and having fun developing great software.
John
Follow me on Twitter: @john_canessa