Comparison: JMS Message Queue vs. Apache Kafka
This article explores the differences, trade-offs, and architectures of JMS message brokers and Kafka deployments.
Join the DZone community and get the full member experience.
Join For FreeComparing JMS-based message queue (MQ) infrastructures and Apache Kafka-based data streaming is a widespread topic. Unfortunately, the battle is an apple-to-orange comparison that often includes misinformation and FUD from vendors. This article explores the differences, trade-offs, and architectures of JMS message brokers and Kafka deployments. Learn how to choose between JMS brokers like IBM MQ or RabbitMQ and open-source Kafka or serverless cloud services like Confluent Cloud.
Motivation: The Battle of Apples vs. Oranges
I have to discuss the differences and trade-offs between JMS message brokers and Apache Kafka every week in customer meetings. What annoys me most is the common misunderstandings and (sometimes) intentional FUD in various blogs, articles, and presentations about this discussion.
I recently discussed this topic with Clement Escoffier from Red Hat in the "Coding over Cocktails" Podcast: JMS vs. Kafka: Technology Smackdown. A great conversation with more agreement than you might expect from such an episode where I picked the "Kafka proponent" while Clement took over the role of the "JMS proponent."
These aspects motivated me to write a blog series about "JMS, Message Queues, and Apache Kafka":
- THIS POST: 10 Comparison Criteria for JMS Message Broker vs. Apache Kafka Data Streaming
- Alternatives for a Dead Letter Queue (DQL) in Apache Kafka
- Implementing the Request-Reply Pattern with Apache Kafka
- UPCOMING: A Decision Tree for Choosing the Right Messaging System (JMS vs. Apache Kafka)
- UPCOMING: From JMS Message Broker to Apache Kafka: Integration, Migration, and/or Replacement
Special thanks to my colleague and long-term messaging and data streaming expert Heinz Schaffner for technical feedback and review of this series. He has worked for TIBCO, Solace, and Confluent for 25 years.
10 Comparison Criteria: JMS vs. Apache Kafka
This article explores ten comparison criteria. The goal is to explain the differences between message queues and data streaming, clarify some misunderstandings about what an API or implementation is, and give some technical background to do your evaluation to find the right tool for the job.
The list of products and cloud services is long for JMS implementations and Kafka offerings. A few examples:
- JMS implementations of the JMS API (open source and commercial offerings): Apache ActiveMQ, Apache Qpid (using AMQP), IBM MQ (formerly MQSeries, then WebSphere MQ), JBoss HornetQ, Oracle AQ, RabbitMQ, TIBCO EMS, TIBCO Cloud Messaging, Solace, and so on.
- Apache Kafka products, cloud services, and rewrites (beyond the valid option of using just open-source Kafka): Confluent, Cloudera, Amazon MSK, Red Hat, Redpanda, Azure Event Hubs, and so on.
Here are the criteria for comparing JMS message brokers vs. Apache Kafka and its related products/cloud services:
- Message broker vs. data streaming platform
- API Specification vs. open-source protocol implementation
- Transactional vs. analytical workloads
- Push vs. pull message consumption
- Simple vs. powerful and complex API
- Storage for durability vs. true decoupling
- Server-side data-processing vs. decoupled continuous stream processing
- Complex operations vs. serverless cloud
- Java/JVM vs. any programming language
- Single deployment vs. multi-region (including hybrid and multi-cloud) replication
Let's now explore the ten comparison criteria.
1. Message Broker vs. Data Streaming Platform
TL;DR: JMS message brokers provide messaging capabilities to produce and consume messages. Apache Kafka is a data streaming platform that combines messaging, storage, data integration, and stream processing capabilities.
The most important aspect first: The comparison of JMS and Apache Kafka is an apple-to-orange comparison for several reasons. I would even further say that not both can be fruit, as they are so different from each other.
JMS API (and Implementations Like IBM MQ, RabbitMQ, et al)
JMS (Java Message Service) is a Java application programming interface (API) that provides generic messaging models. The API handles the producer-consumer problem, which can facilitate the sending and receiving of messages between software systems.
Therefore, the central capability of JMS message brokers (that implement the JMS API) is to send messages from a source application to another destination in real-time. That's it. And if that's what you need, then JMS is the right choice for you! But keep in mind that projects must use additional tools for data integration and advanced data processing tasks.
Apache Kafka (Open Source and Vendors Like Confluent, Cloudera, Red Hat, Amazon, et al)
Apache Kafka is an open-source protocol implementation for data streaming. It includes:
- Apache Kafka is the core for distributed messaging and storage. High throughput, low latency, high availability, secure.
- Kafka Connect is an integration framework for connecting external sources/destinations to Kafka.
- Kafka Streams is a simple Java library that enables streaming application development within the Kafka framework.
This combination of capabilities enables the building of end-to-end data pipelines and applications. That's much more than what you can do with a message queue.
2. JMS API Specification vs. Apache Kafka Open-Source Protocol Implementation
TL;DR: JMS is a specification that vendors implement and extend in their opinionated way. Apache Kafka is the open-source implementation of the underlying specified Kafka protocol.
It is crucial to clarify the terms first before you evaluate JMS and Kafka:
- Standard API: Specified by industry consortiums or other industry-neutral (often global) groups or organizations specify standard APIs. Requires compliance tests for all features and complete certifications to become standard-compliant. Example: OPC-UA.
- De facto standard API: Originates from an existing successful solution (an open-source framework, a commercial product, or a cloud service). Examples: Amazon S3 (proprietary from a single vendor). Apache Kafka (open source from the vibrant community).
- API specification: A specification document to define how vendors can implement a related product. There are no complete compliance tests or complete certifications for the implementation of all features. The consequence is a "standard API" but no portability between implementations. Example: JMS. Specifically for JMS, note that in order to be able to use the compliance suite for JMS, a commercial vendor has to sign up to very onerous reporting requirements towards Oracle.
The alternative kinds of standards have trade-offs. If you want to learn more, check out how Apache Kafka became the de facto standard for data streaming in the last few years.
Portability and migrations became much more relevant in hybrid and multi-cloud environments than in the past decades where you had your workloads in a single data center.
JMS Is a Specification for Message-oriented Middleware
JMS is a specification currently maintained under the Java Community Process as JSR 343. The latest (not yet released) version JMS 3.0 is under early development as part of Jakarta EE and rebranded to Jakarta Messaging API. Today, JMS 2.0 is the specification used in prevalent message broker implementations. Nobody knows where JMS 3.0 will go at all. Hence, this post focuses on the JMS 2.0 specification to solve real-world problems today.
I often use the term "JMS message broker" in the following sections as JMS (i.e., the API) does not specify or implement many features you know in your favorite JMS implementation. Usually, when people talk about JMS, they mean JMS message broker implementations, not the JMS API specification.
JMS Message Brokers and the JMS Portability Myth
The JMS specification was developed to provide a common Java library to access different messaging vendor’s brokers. It was intended to act as a wrapper to the messaging vendor’s proprietary APIs in the same way JDBC provided similar functionality for database APIs.
Unfortunately, this simple integration turned out not to be the case. The migration of the JMS code from one vendor’s broker to another is quite complex for several reasons:
- Not all JMS features are mandatory (security, topic/queue labeling, clustering, routing, compression, etc.)
- There is no JMS specification for transport
- No specification to define how persistence is implemented
- No specification to define how fault tolerance or high availability is implemented
- Different interpretations of the JMS specification by different vendors result in potentially other behaviors for the same JMS functions
- No specification for security
- There is no specification for value-added features in the brokers (such as topic to queue bridging, inter-broker routing, access control lists, etc.)
Therefore, simple source code migration and interoperability between JMS vendors is a myth! This sounds crazy, doesn't it?
Vendors provide a great deal of unique functionality within the broker (such as topic-to-queue mapping, broker routing, etc.) that provide architectural functionality to the application but are part of the broker functionality and not the application or part of the JMS specification.
Apache Kafka Is an Open-source Protocol Implementation for Data Streaming
Apache Kafka is an implementation to do reliable and scalable data streaming in real-time. The project is open-source and available under Apache 2.0 license, and is driven by a vast community.
Apache Kafka is NOT a standard like OPC-UA or a specification like JMS. However, Kafka at least provides the source code reference implementation, protocol and API definitions, etc.
Kafka established itself as the de facto standard for data streaming. Today, more than 100,000 organizations use Apache Kafka. The Kafka API became the de facto standard for event-driven architectures and event streaming. It has use cases across all industries and infrastructure, including various kinds of transactional and analytic workloads. Edge, hybrid, multi-cloud.
Now, hold on. I used the term Kafka API in the above section. Let's clarify this: As discussed, Apache Kafka is an implementation of a distributed data streaming platform including the server-side and client-side and various APIs for producing and consuming events, configuration, security, operations, etc. The Kafka API is relevant, too, as Kafka rewrites like Azure Event Hubs and Redpanda use it.
Portability of Apache Kafka: Yet Another Myth?
If you use Apache Kafka as an open-source project, this is the complete Kafka implementation. Some vendors use the full Apache Kafka implementation and build a more advanced product around it.
Here, the migration is super straightforward, as Kafka is not just a specification that each vendor implements differently. Instead, it is the same code, libraries, and packages.
For instance, I have seen several successful migrations from Cloudera to Confluent deployments or from self-managed Apache Kafka open-source infrastructure to serverless Confluent Cloud.
The Kafka API: Kafka Rewrites Like Azure Event Hubs, Redpanda, Apache Pulsar
With the global success of Kafka, some vendors and cloud services did not build a product on top of the Apache Kafka implementation. Instead, they made their implementation on top of the Kafka API. The underlying implementation is proprietary (like in Azure's cloud service Event Hubs) or open-source (like Apache Pulsar's Kafka bridge or Redpanda's rewrite in C++).
Be careful and analyze if vendors integrate the whole Apache Kafka project or rewrote the complete API. Contrary to the battle-tested Apache Kafka project, a Kafka rewrite using the Kafka API is a completely new implementation!
Many vendors even exclude some components or APIs (like Kafka Connect for data integration or Kafka Streams for stream processing) completely or exclude critical features like exactly-once semantics or long-term storage in their support terms and conditions.
It is up to you to evaluate the different Kafka offerings and their limitations. Recently, I compared Kafka vendors such as Confluent, Cloudera, Red Hat, or Amazon MSK and related technologies like Azure Event Hubs, AWS Kinesis, Redpanda, or Apache Pulsar.
Just battle-test the requirements by yourself. If you find a Kafka-to-XYZ bridge with less than a hundred lines of code, or if you find a .exe Windows Kafka server download from a middleware vendor. Be skeptical! :-)
All that glitters is not gold. Some frameworks or vendors sound too good to be true. Just saying you support the Kafka API, you provide a fully managed serverless Kafka offering, or you scale much better is not trustworthy if you are constantly forced to provide fear, uncertainty, and doubt (FUD) on Kafka and that you are much better. For instance, I was annoyed by Pulsar always trying to be better than Kafka by creating a lot of FUDs and myths in the open-source community. I responded in my Apache Pulsar vs. Kafka comparison two years ago. FUD is the wrong strategy for any vendor. It does not work. For that reason, Kafka's adoption still grows like crazy while Pulsar grows much slower percentage-wise (even though the download numbers are on a much lower level anyway).
3. Transactional vs. Analytical Workloads
TL;DR: A JMS message broker provides transactional capabilities for low volumes of messages. Apache Kafka supports low and high volumes of messages supporting transactional and analytical workloads.
JMS: Session and Two-phase Commit (XA) Transactions
Most JMS message brokers have good support for transactional workloads.
A transacted session supports a single series of transactions. Each transaction groups a set of produced messages and a set of consumed messages into an atomic unit of work.
Two-phase commit transactions (XA transactions) work on a limited scale. They are used to integrate with other systems like Mainframe CICS / DB2 or Oracle database. But it is hard to operate and not possible to scale beyond a few transactions per second.
It is important to note that support for XA transactions is not mandatory with the JMS 2.0 specification. This differs from the session transaction.
Kafka: Exactly-once Semantics and Transaction API
Kafka is a distributed, fault-tolerant system that is resilient by nature (if you deploy and operate it correctly). No downtime and no data loss can be guaranteed, like in your favorite database, mainframe, or other core platforms.
And even better: Kafka’s Transaction API, i.e., Exactly-Once Semantics (EOS), has been available since Kafka 0.11 (GA’ed many years ago). EOS makes building transactional workloads even easier as you don’t need to handle duplicates anymore.
Kafka supports atomic writes across multiple partitions through the transactions API. This allows a producer to send a batch of messages to multiple partitions. Either all messages in the batch are eventually visible to any consumer, or none are ever visible to consumers.
Kafka transactions work very differently than JMS transactions. But the goal is the same: Each consumer receives the produced event exactly once. Find more details in the blog post "Analytics vs. Transactions in Data Streaming with Apache Kafka."
4. Push vs. Pull Message Consumption
TL;DR: JMS message brokers push messages to consumer applications. Kafka consumers pull messages providing true decoupling and backpressure handling for independent consumer applications.
Pushing messages seems to be the obvious choice for a real-time messaging system like JMS-based message brokers. However, push-based messaging has various drawbacks regarding decoupling and scalability.
JMS expects the broker to provide back pressure and implement a "pre-fetch" capability, but this is not mandatory. If used, the broker controls the backpressure, which you cannot control.
With Kafka, the consumer controls the backpressure. Each Kafka consumer consumes events in real-time, batch, or only on demand — in the way the particular consumer supports and can handle the data stream. This is an enormous advantage for many inflexible and non-elastic environments.
So while JMS has some kind of backpressure, the producer stops if the queue is full. In Kafka, you control the backpressure on the consumer. There is no way to scale a producer with JMS (as there are no partitions in a JMS queue or topic).
JMS consumers can be scaled, but then you lose guaranteed ordering. Guaranteed ordering in JMS message brokers only works via a single producer, single consumer, and transaction.
5. Simple JMS API vs. Powerful and Complex Kafka API
TL;DR: The JMS API provides simple operations to produce and consume messages. Apache Kafka has a more granular API that brings additional power and complexity.
JMS vendors hide all the cool stuff in the implementation under the spec. You only get the 5% (no control, built by the vendor). You need to make the rest by yourself. On the other side, Kafka exposes everything. Most developers only need 5%.
In summary, be aware that JMS message brokers are built to send messages from a data source to one or more data sinks. Kafka is a data streaming platform that provides many more capabilities, features, event patterns, and processing options; and a much larger scale. With that in mind, it is no surprise that the APIs are very different and have different complexity.
If your use case requires just sending a few messages per second from A to B, the JMS is the right choice and simple to use! If you need a streaming data hub at any scale, including data integration and data processing, that's only Kafka.
Asynchronous Request-reply vs. Data in Motion
One of the most common wishes of JMS developers is to use are request-response function in Kafka. Note that this design pattern is different in messaging systems from an RPC (remote procedure call) as you know it from legacy tools like Corba or web service standards like SOAP/WSDL or HTTP. Request-reply in messaging brokers is an asynchronous communication that leverages a correlation ID.
Asynchronous messaging to get events from a producer (say a mobile app) to a consumer (say a database) is a very traditional workflow. No matter if you do fire-and-forget or request-reply. You put data at rest for further processing. JMS supports request-reply out-of-the-box. The API is very simple.
Data in motion with event streaming continuously processes data. The Kafka log is durable. The Kafka application maintains and queries the state in real-time or in batch. Data streaming is a paradigm shift for most developers and architects. The design patterns are very different. Don't try to reimplement your JMS application within Kafka using the same pattern and API. That is likely to fail! That is an anti-pattern.
Request-reply is inefficient and can suffer a lot of latency depending on the use case. HTTP or better gRPC is suitable for some use cases. Request-reply is replaced by the CQRS (Command and Query Responsibility Segregation) pattern with Kafka for streaming data. CQRS is not possible with JMS API, since JMS provides no state capabilities and lacks event sourcing capability.
A Kafka Example for the Request-response Pattern
CQRS is the better design pattern for many Kafka use cases. Nevertheless, the request-reply pattern can be implemented with Kafka, too. But differently. Trying to do it like in a JMS message broker (with temporary queues etc.) will ultimately kill the Kafka cluster (because it works differently).
The Spring project shows how you can do better. The Kafka Spring Boot Kafka Template libraries have a great example of the request-reply pattern built with Kafka.
Check out "org.springframework.kafka.requestreply.ReplyingKafkaTemplate". It creates request/reply applications using the Kafka API easily. The example is interesting since it implements the asynchronous request/reply, which is more complicated to write if you are using, for example, JMS API). Another nice DZone article talks about synchronous request/reply using Spring Kafka templates.
The Spring documentation for Kafka Templates has a lot of details about the Request/Reply pattern for Kafka. So if you are using Spring, the request/reply pattern is pretty simple to implement with Kafka. If you are not using Spring, you can learn how to do request-reply with Kafka in your framework.
6. Storage for Durability vs. True Decoupling
TL;DR: JMS message brokers use a storage system to provide high availability. The storage system of Kafka is much more advanced to enable long-term storage, back-pressure handling and replayability of historical events.
Kafka Storage Is More Than Just the Persistence Feature You Know from JMS
When I explain the Kafka storage system to experienced JMS developers, I almost always get the same response: "Our JMS message broker XYZ also has storage under the hood. I don't see the benefit of using Kafka!"
JMS uses an ephemeral storage system, where messages are only persisted until they are processed. Long-term storage and replayability of messages are not a concept JMS was designed for.
The core Kafka principles of append-only logs, offsets, guaranteed ordering, retention time, compacted topics, and so on provide many additional benefits beyond the durability guarantees of a JMS. Backpressure handling, true decoupling between consumers, the replayability of historical events, and more are huge differentiators between JMS and Kafka.
Check the Kafka docs for a deep dive into the Kafka storage system. I don't want to touch on how Tiered Storage for Kafka is changing the game even more by providing even better scalability and cost-efficient long-term storage within the Kafka log.
7. Server-side Data Processing With JMS vs. Decoupled Continuous Stream Processing With Kafka
TL;DR: JMS message brokers provide simple server-side event processing, like filtering or routing based on the message content. Kafka brokers are dumb. Its data processing is executed in decoupled applications/microservices.
Server-side JMS Filtering and Routing
Most JMS message brokers provide some features for server-side event processing. These features are handy for some workloads!
Just be careful that server-side processing usually comes with a cost. For instance:
- JMS Pre-filtering scalability issues: The broker has to handle so many things. This can kill the broker in a hidden fashion
- JMS Selectors (= routing) performance issues: It kills 40-50% of performance
Again, sometimes, the drawbacks are acceptable. Then this is a great functionality.
Kafka: Dumb Pipes and Smart Endpoints
Kafka intentionally does not provide server-side processing. The brokers are dumb. The processing happens at the smart endpoints. This is a very well-known design pattern: Dumb pipes and smart endpoints.
The drawback is that you need separate applications/microservices/data products to implement the logic. This is not a big issue in serverless environments (like using a ksqlDB process running in Confluent Cloud for data processing). It gets more complex in self-managed environments.
However, the massive benefit of this architecture is the true decoupling between applications/technologies/programming languages, separation of concerns between business units for building business logic and operations of infrastructure, and the much better scalability and elasticity.
Would I like to see a few server-side processing capabilities in Kafka, too? Yes, absolutely. Especially for small workloads, the performance and scalability impact should be acceptable! Though, the risk is that people misuse the features then. The future will show if Kafka will get there or not.
8. Complex Operations vs. Serverless Cloud
TL;DR: Self-managed operations of scalable JMS message brokers or Kafka clusters are complex. Serverless offerings (should) take over the operations burden.
Operating a Cluster Is Complex, No Matter If JMS or Kafka
A basic JMS message broker is relatively easy to operate (including active/passive setups). However, this limits scalability and availability. The JMS API was designed to talk to a single broker or active/passive for high availability. This concept covers the application domain.
More than that (= clustering) is very complex with JMS message brokers. More advanced message broker clusters from commercial vendors are more powerful but much harder to operate.
Kafka is a powerful distributed system. Therefore, operating a Kafka cluster is not easy by nature. Cloud-native tools like an operator for Kubernetes take over some burdens like rolling upgrades or handling fail-over.
Both JMS message brokers and Kafka clusters are the more challenging, the more scale and reliability your SLAs demand. The JMS API is not specified for a central data hub (using a cluster). Kafka is intentionally built for the strategic enterprise architecture, not just for a single business application.
Fully Managed Serverless Cloud to the Rescue
As the JMS API was designed to talk to a single broker, it is hard to build a serverless cloud offering that provides scalability. Hence, in JMS cloud services, the consumer has to set up the routing and role-based access control to the specific brokers. Such a cloud offering is not serverless but cloud-washing! But there is no other option as the JMS API is not like Kafka with one big distributed cluster.
In Kafka, the situation is different. As Kafka is a scalable distributed system, cloud providers can build cloud-native serverless offerings. Building such a fully managed infrastructure is still super hard. Hence, evaluate the product, not just the marketing slogans!
Every Kafka cloud service is marketed as "fully managed" or "serverless" but most are NOT. Instead, most vendors just provision the infrastructure and let you operate the cluster and take over the support risk. On the other side, some fully managed Kafka offerings are super limited in functionality (like allowing a very limited number of partitions).
Some cloud vendors even exclude Kafka support from their Kafka cloud offerings. Insane, but true. Check the terms and conditions as part of your evaluation.
9. Java/JVM vs. Any Programming Language
TL;DR: JMS focuses on the Java ecosystem for JVM programming languages. Kafka is independent of programming languages.
As the name JMS (=Java Message Service) says: JMS was written only for Java officially. Some broker vendors support their own APIs and clients. These are proprietary to that vendor. Almost all severe JMS projects I have seen in the past use Java code.
Apache Kafka also only provides a Java client. But vendors and the community provide other language bindings for almost every programming language, plus a REST API for HTTP communication for producing/consuming events to/from Kafka. For instance, check out the blog post "12 Programming Languages Walk into a Kafka Cluster" to see code examples in Java, Python, Go, .NET, Ruby, node.js, Groovy, and so on.
The true decoupling of the Kafka backend enables very different client applications to speak with each other, no matter what programming languages one uses. This flexibility allows for building a proper domain-driven design (DDD) with a microservices architecture leveraging Kafka as the central nervous system.
10. Single JMS Deployment vs. Multi-region (Including Hybrid and Multi-cloud) Kafka Replication
TL;DR: The JMS API is a client specification for communication between the application and the broker. Kafka is a distributed system that enables various architectures for hybrid and multi-cloud use cases.
JMS is a client specification, while multi-data center replication is a broker function. I won't go deep here and put it simply: JMS message brokers are not built for replication scenarios across regions, continents, or hybrid/multi-cloud environments.
Multi-cluster and cross-data center deployments of Apache Kafka have become the norm rather than an exception. Various scenarios require multi-cluster Kafka solutions. Specific requirements and trade-offs need to be looked at.
Kafka technologies like MirrorMaker (open source) or Confluent Cluster Linking (commercial) enable use cases such as disaster recovery, aggregation for analytics, cloud migration, mission-critical stretched deployments and global Kafka deployments.
JMS and Kafka Solve Distinct Problems
The 10 comparison criteria show that JMS and Kafka are very different things. While both overlap (e.g., messaging, real-time, mission-critical), they use different technical capabilities, features, and architectures to support additional use cases.
In short, use a JMS broker for simple and low-volume messaging from A to B. Kafka is usually a real-time data hub between many data sources and data sinks. Many people call it the central real-time nervous system of the enterprise architecture.
The data integration and data processing capabilities of Kafka at any scale with true decoupling and event replayability are the major differences from JMS-based MQ systems.
However, especially in the serverless cloud, don’t fear Kafka being too powerful (and complex). Serverless Kafka projects often start very cheaply at a very low volume, with no operations burden. Then it can scale with your growing business without the need to re-architect the application.
Understand the technical differences between a JMS-based message broker and data streaming powered by Apache Kafka. Evaluate both options to find the right tool for the problem. Within messaging or data streaming, do further detailed evaluations. Every message broker is different even though they all are JMS compliant. In the same way, all Kafka products and cloud services are different regarding features, support, and cost.
Do you use JMS-compliant message brokers? What are the use cases and limitations? When did you or do you plan to use Apache Kafka instead?
Published at DZone with permission of Kai Wähner, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments