You’ve seen Java Message Service (JMS) and Advanced Message
Queueing Protocol (AMQP) mentioned in the context of enterprise
messaging — But what are they? Are they related? And what’s their
connection to RabbitMQ?
Lots of questions, but let’s start with the first one: What are they?
Both JMS and AMQP enable message-based communication in
different contexts— JMS is tailored for Java applications,
providing a standardized way for Java programs to communicate
asynchronously. AMQP, on the other hand, is an open standard
protocol that supports cross-platform messaging, making it ideal
for connecting systems written in different programming languages
and/or running on different operating systems.
That was just an overview, now, let’s peel the layers back…
Java Message Service(JMS)
The
JMS specification
originates and is still heavily used in the Java ecosystem - Why?
Building distributed Java applications (and distributed applications in general)
has several benefits but they are relatively more complex to build. For example,
how do you coordinate and get the services to communicate?
In distributed architectures, services often communicate asynchronously via
lightweight messaging, a practice that holds in the Java ecosystem.
Previously, Java applications used different messaging systems, each with its
own API, making it hard to switch between them— a standardized way to
handle messaging was needed— enter JMS.
JMS, specifically designed for Java applications, defines a set of high-level APIs that these applications use to communicate with each other via messages.
By providing a common interface, JMS aims to standardize messaging between
applications in the Java ecosystem. This makes it easy to replace any
JMS-based message broker or client library with another one,
provided both brokers support the underlying protocol used
for communication.
JMS architecture
Working with JMS is pretty straightforward. Client applications called producers
would publish messages to a destination on a JMS provider or broker.
Other client applications called consumers would subscribe to a destination
and receive messages from the broker.
JMS is just an API,
meaning it provides a standard set of commands for sending and receiving
messages but does not handle the actual transport of the messages.
When Producers and Consumers connect to a JMS broker, the API hides the
details of the underlying communication protocol used to transmit messages.
The underlying communication protocol used to transmit messages to a
JMS broker/provider varies from broker to broker . For instance,
ActiveMQ supports a range of protocols such as OpenWire, STOMP,
XMPP, MQTT, WSIF, AMQP, and even REST (over HTTP).
The flaws of JMS
While JMS unarguably standardizes messaging within the Java ecosystem,
it gets a bit tricky when a non-JMS client needs to connect to a
JMS broker — Picture this scenario:
On one end you have a producer written in Java, publishing messages
to ActiveMQ via JMS over the OpenWire protocol. On the other end,
you have a consumer written in Python consuming messages from ActiveMQ
over the STOMP protocol.
In the illustration above, the Java Producer uses the ActiveMQ JMS
client to communicate with the broker via JMS over OpenWire.
The Python Consumer uses the PyActiveMQ client to talk to the
broker over STOMP — Since it’s impossible to have a JMS-based Python client.
For this cross-platform scenario to be implemented with a JMS-centric
broker, the broker has to provide a message bridge that can parse the
OpenWire packets to STOMP packets. Thus, you cannot easily replace one
broker with any other, as the new broker should also support the transformations
that the existing broker was supporting.
The scenario above illustrates that JMS isn’t the right solution
for cross-platform interoperability. While this is doable, it is not
the easiest thing to do.
This is exactly why AMQP was created; to standardize cross-platform
messaging interoperability. Let’s talk about it.
Advanced Message Queueing Protocol (AMQP)
From the outset, AMQP was intended to be an open standard created to
standardize cross-platform messaging interoperability.
By being open, any vendor could implement the AMQP standard in
their broker and client libraries could be built in any programming
language and by anyone.
Essentially, whereas JMS is Java-focused, AMQP provides a specification
for an industry-standard wire-level messaging protocol. Consequently,
any client that implements the protocol, can talk to any broker that
also implements the AMQP protocol.
Messaging clients using AMQP are completely agnostic to the AMQP message
broker being used. The same goes for the AMQP-based message brokers — They
allow clients written in any programming language and running on any
operating system to connect to them.
Cycling back to the previous scenario we considered — On one end,
you could have an AMQP-based
Java client library
publishing messages to an AMQP-based message broker like RabbitMQ.
Then on the other end, you could also have an AMQP-based Python client
library consuming messages from the AMQP broker.
With AMQP, the broker does not have to provide a message bridge
for some protocol transformation — think interoperability.
AMQP architecture
With an AMQP message broker, typically:
A service publishes messages to the message broker.
The messages published go to an exchange first, then to a queue. Think of an exchange as the entry point into the message broker, whose job is to route the message from the producer into the correct queue.
Exchanges route messages to queues with the help of bindings, which are rules that define the relationship between an exchange and a queue. So when the exchange receives a message It looks at that message’s properties and then routes it to the correct queue.
A message typically has a routing key, which indicates what queue it is supposed to land in.
That message is then stored in a queue until it is consumed.
That is done by the consuming service, where a consumer subscribes to a queue and receives messages from it. Once a message is consumed, it’s removed from the queue.
This binding mechanism, combined with various exchange types, allows for
complex routing logic in AMQP which, in turn, opens up doors for various
messaging use cases like publish/subscribe, point-to-point, and request/reply
patterns in AMQP.
Now that we know what JMS and AMQP are, let’s take our exploration a step further.
JMS & AMQP: Are they related?
JMS and AMQP are related to an extent since they are both used to enable message-based
communication between different parts of a system, but that’s where the similarities end.
JMS and AMQP have distinct roles and features. While JMS is simply a Java API specification,
AMQP is a protocol that defines how messages are formatted and transmitted across the network,
allowing for interoperability between systems written in different programming languages.
The core difference lies in their scope and usage: JMS is specific to the Java ecosystem,
making it ideal for enterprise Java applications, while AMQP is language-agnostic and
designed for broader interoperability and cross-platform messaging.
The title of this blog says “JMS & AMQP in RabbitMQ”, but we haven’t said a thing about
RabbitMQ so far — Now, let’s go in that direction.
JMS & AMQP: The connection to RabbitMQ
For those new to it,
RabbitMQ is simply a popular open-source message broker.
A message broker inherently enables different applications and systems to communicate
with each other using messages.
Using JMS with RabbitMQ
RabbitMQ supports the Java Message Service (JMS) API, allowing JMS-based Java applications
to use RabbitMQ as a messaging broker. To use JMS with RabbitMQ, you will need to do the following:
You can then connect to RabbitMQ and publish or consume
messages in your producer and consumer respectively
When Java developers use JMS with RabbitMQ, they can leverage the familiar JMS API
to handle messaging tasks such as sending, receiving, and processing messages
asynchronously. This makes it easier to integrate RabbitMQ into existing Java-based
systems that already use JMS.
Using AMQP with RabbitMQ
RabbitMQ is natively built on the Advanced Message Queuing
Protocol (AMQP). Consequently, using AMQP with RabbitMQ
is straightforward.
You simply need an AMQP client library that is available across
different platforms and programming languages.
This makes RabbitMQ an excellent choice for systems that require
cross-language interoperability.
Conclusion
RabbitMQ supports both JMS and AMQP. This means Java applications can
use RabbitMQ with the familiar JMS API, while other systems can
leverage AMQP for reliable and interoperable messaging.
Feel free to create a RabbitMQ instance on CloudAMQP and
try out JMS.