Blog

JMS vs Kafka

Key Takeaways

  • JMS is a Java messaging specification and API; Kafka is a distributed event-streaming platform. Compare a particular JMS provider with Kafka, not an API with a cluster.
  • JMS queues distribute messages among competing consumers, while topic subscriptions support publish/subscribe delivery. Kafka consumer groups independently track their progress through retained records.
  • Kafka replay is bounded by retention and cleanup policies. JMS durable subscriptions preserve pending messages for offline subscribers, but aren't a general historical replay mechanism.
  • Neither platform automatically makes database writes, payments, or other external effects happen exactly once. Define transaction boundaries and make retryable operations idempotent.

The essential difference in JMS vs Kafka is the abstraction: JMS defines how Java applications exchange messages through a provider, while Kafka supplies a platform built around retained, partitioned event logs. Choose broker-managed messaging when you need queue and subscription semantics; choose an event log when independent consumers need to process and replay the same history.

This isn't an old-versus-modern contest. Start with the delivery contract your application needs, then evaluate implementations, failure behavior, and operational cost.

What is Java Message Service (JMS)?

JMS standardizes a Java API for creating, sending, and receiving messages. A messaging provider implements that API and supplies the messaging infrastructure behind it.

JMS as a specification: Jakarta Messaging and providers

The specification continues as Jakarta Messaging. Jakarta Messaging 3.0 changed the API namespace from javax.jms to jakarta.jms; your application, provider client, and application-server integration must use compatible APIs. Changing imports alone isn't a complete migration.

Examples of provider support include:

These are documented compatibility examples, not interchangeable dependencies. JMS standardizes the programming interface, not a wire protocol, deployment model, or every provider-specific feature. Routing extensions, administration, and failover behavior still require provider-specific evaluation.

Two qualifications matter when comparing other products. Amazon SQS offers a JMS client library for queue-oriented messaging, with a documented supported feature set—not a blanket promise of full JMS functionality. RabbitMQ's official documentation explicitly describes its server plus RabbitMQ JMS client as a JMS provider implementation, with a companion topic-exchange plugin. Calling RabbitMQ simply “not a JMS provider” would also be misleading.

Understanding messages

A message carries application data plus metadata. JMS provides text, bytes, map, stream, and object message types; JSON and XML can be carried as text. An ObjectMessage contains a serializable Java object, not an arbitrary POJO.

Prefer explicit, versioned payload contracts over serialized domain objects. Object serialization introduces class compatibility and deserialization concerns; see Java serialization in depth and your provider's object-deserialization controls.

Operational mechanics of JMS

A producer sends to a destination—a queue or topic—and the provider manages delivery to consumers. Consumers can receive synchronously or register a message listener. The broker decouples producer and consumer availability where the destination and delivery configuration preserve pending messages; “asynchronous messaging” doesn't mean every client operation is nonblocking.

JMS communication models

Point-to-point queues

A queue can have multiple competing consumers. Each message is intended for one consumer, rather than broadcast to every consumer; failed processing can lead to redelivery. The specification doesn't define a universal distribution algorithm across queue consumers.

Publish/subscribe topics

Each topic subscription receives its own matching messages. An ordinary nondurable subscription doesn't preserve messages published while it has no consumer; an established durable subscription retains pending messages while its consumers are inactive, subject to expiration. Shared subscriptions let multiple consumers divide one subscription's messages.

What is Kafka?

Apache Kafka is a distributed event-streaming platform. Producers append records to topic partitions—ordered logs distributed across brokers—and consumers read those records without deleting them merely by consuming them. Partition replication provides the basis for fault tolerance.

Kafka topics, consumer groups, and offsets

In a conventional Kafka consumer group, each partition is assigned to at most one consumer at a time. Consumers in the group divide the work; separate groups read the same retained records independently. Each group maintains committed offsets describing where consumption should resume.

JMS broker                         Kafka topic: orders
                                   retained partition logs
Producer --> Queue                 P0: [0][1][2][3]...
               |                   P1: [0][1][2]...
               +--> Worker A
               +--> Worker B       Group: billing
One competing worker per delivery    P0 -> Consumer A
                                     P1 -> Consumer B
Producer --> Topic
               +--> Subscription X Group: analytics
               +--> Subscription Y   P0, P1 -> Consumer C
                                   independent committed offsets

The diagram shows the standard consumer-group model. Our Kafka architecture explanation expands on partitions, brokers, and replication.

Retention, deletion, and compaction

Kafka's cleanup.policy supports deletion, compaction, or both. Under deletion, time-based retention and per-partition size limits make old log segments eligible for removal. Compaction instead removes superseded values for a key, preserving a latest-state view rather than every historical update.

Replay therefore means “reread records that still exist.” A slow consumer doesn't prevent retention cleanup, and a compacted topic isn't a complete audit history. Review the topic cleanup and retention settings before promising recovery or rebuild windows.

JMS vs Kafka: Delivery, Transactions, and Scale

Consuming messages: acknowledgements versus offsets

For nontransacted JMS sessions, the acknowledgement mode controls when consumption is considered complete:

  • AUTO_ACKNOWLEDGE: acknowledgement follows a successful synchronous receive or successful listener return. With synchronous receives, acknowledgement can therefore precede your business processing.
  • CLIENT_ACKNOWLEDGE: calling Message.acknowledge() acknowledges all messages consumed by that session, not just that message.
  • DUPS_OK_ACKNOWLEDGE: lazy acknowledgement permits duplicate delivery after failure.

These are the session acknowledgement rules; managed Jakarta EE components impose additional restrictions. Persistent delivery addresses provider-failure durability, while expiration and redelivery configuration remain separate concerns.

Kafka's committed offset is a restart position, not an instruction to delete a record. Committing before business processing can skip unfinished work after a crash. Processing first and committing afterward supports at-least-once processing, but a crash between those steps can repeat an already completed effect.

JMS with explicit acknowledgement:
receive -> database commit -> CRASH -> acknowledge never happens
                                      |
                                      +-- possible redelivery

Kafka with processing before offset commit:
poll -> database commit -> CRASH -> offset commit never happens
                                   |
                                   +-- possible reprocessing

Make handlers idempotent: repeating the same operation should not repeat its business effect. For database-backed work, consider an inbox table with a unique event identifier, recorded in the same database transaction as the business update.

Transactions have different boundaries

A JMS local transaction groups sends and receives within one session: commit acknowledges consumed messages and publishes produced messages; rollback recovers consumed messages. Coordinating a database requires a supported distributed transaction arrangement, such as XA with a transaction manager—not merely a transacted JMS session.

Kafka's idempotent producer prevents duplicates caused by producer retries within its supported scope; it doesn't deduplicate arbitrary application resends. Kafka transactions can atomically publish across Kafka partitions and include consumed offsets for Kafka-to-Kafka processing. Downstream consumers must use isolation.level=read_committed to exclude aborted transactional records.

Those transactions don't automatically include a database, payment API, or JMS broker. For database-plus-event publishing, a transactional outbox records the event alongside the database change, then publishes it separately. Consumers still need duplicate handling.

Scalability and ordering

Kafka orders records within a partition, not across an entire multi-partition topic. Route related events to the same partition—for example, by order ID—and preserve that order in application processing. A heavily used key can concentrate work on one partition.

For conventional consumer groups, partition count bounds the number of consumers with assignments. Adding consumers beyond that doesn't increase partition-level parallelism. Reassignments can interrupt work on affected partitions; incremental rebalance protocols reduce disruption but don't remove the need to handle ownership changes.

JMS isn't globally unordered or universally FIFO. The specification defines ordering for messages sent by a session to a destination, with qualifications including priority, delivery time, and delivery mode. Multiple consumers can finish processing in a different order, and broker scaling depends on the provider's clustering and destination design.

Performance: benchmark the delivery contract

Kafka's sequential-log access and batching support throughput-oriented workloads. Compaction is an optional cleanup policy, not a universal performance optimization. JMS itself has no throughput ceiling: performance belongs to the selected provider, workload, and configuration.

Benchmark equivalent message sizes, persistence, replication, transaction boundaries, consumer work, and hardware. Measure end-to-end latency, sustained throughput, backlog recovery, and behavior during failure—not just producer send rate. Include storage, networking, managed-service charges, and on-call effort in your decision.

When Should You Choose JMS or Kafka?

RequirementStarting pointWhat to validate
Commands and competing workersA JMS queue; also evaluate Kafka share groups if you already operate KafkaRetries, poison-message handling, expiry, and processing order
Request/reply and selective deliveryA JMS providerTemporary destinations, correlation, selectors, and provider routing
Independent downstream applications and historical replayKafka consumer groupsRetention, consumer lag, schemas, and replay side effects
Event sourcing or audit/event feedsEvaluate Kafka as the event backboneComplete-history requirements, cleanup policies, archival, and recovery tests
Existing JMS applications and transaction infrastructureKeep the provider unless requirements justify a changeNamespace compatibility, extensions, and migration cost

JMS supplies selectors, temporary destinations, and reply/correlation headers; Kafka supplies independently readable retained logs, while share groups broaden its queue-like options. The table is a selection guide, not a claim that either platform exclusively owns these workloads.

For a workload-oriented follow-up, read when to use Kafka. There's no reason to migrate a well-operated messaging system merely because another architecture is fashionable.

Using JMS and Kafka Together

A practical design might deliver fulfillment commands through JMS and publish order-status events to Kafka for billing and analytics. Treat the bridge as a failure boundary, not a transparent pipe.

For JMS-to-Kafka forwarding, wait for confirmed Kafka publication before acknowledging or committing the JMS receive. That avoids acknowledging work before the destination accepts it, but a crash between publication and acknowledgement can still produce duplicates. Reversing the bridge has the equivalent problem between JMS publication and Kafka offset commit. Kafka transactions alone don't close either cross-system gap.

  • Carry a stable event ID through retries and deduplicate at the destination.
  • Define how JMS headers and message bodies map to Kafka keys, headers, and serialized values.
  • Preserve entity ordering deliberately; test retries and parallel bridge workers.
  • Specify backoff, quarantine handling, schema conversion failures, and recovery procedures.

Production Checklist

  • Recovery: test crashes before and after business commits, acknowledgements, and offset commits. Our Spring Kafka testing examples provide a starting point for Kafka-side tests.
  • Operations: monitor JMS queue depth and redelivery, plus Kafka consumer lag, replication health, and storage headroom. Kafka documents its relevant monitoring metrics.
  • Contracts: test schema changes against older consumers and retained records before deploying.
  • Security: require encrypted transport, scoped credentials, destination permissions, and safe deserialization.
  • Capacity: test the backlog produced by your longest expected outage, including recovery while new traffic continues.

FAQ

Can JMS and Kafka be used together?

Yes. Use a bridge or connector to move messages between them, but explicitly design for duplicates, ordering, retries, and payload conversion. A successful write to one system and acknowledgement in the other aren't automatically atomic.

Does Kafka support transactions like JMS?

Both support transactions, but their boundaries differ. Kafka transactions can combine Kafka writes and consumed offsets, while JMS local transactions combine messaging operations within a session. Including a database or another messaging system requires additional coordination.

What's the future of JMS in modern applications?

The specification continues as Jakarta Messaging, and providers offer different levels of JMS and Jakarta Messaging compatibility. Evaluate it for new applications when its delivery model and ecosystem fit; age alone isn't a reason to replace it with Kafka.

Is Kafka a replacement for JMS?

Not as a drop-in replacement. Kafka is a platform, while JMS is an API specification, and migrating a JMS application requires checking its delivery, transaction, routing, and request/reply requirements. Kafka share groups add queue-like consumption but don't make Kafka a JMS API implementation.

How do Kafka offsets differ from JMS acknowledgements?

A committed Kafka consumer-group offset records where processing should resume without deleting the underlying records. A JMS acknowledgement marks messages as consumed for the queue or subscription. Either can leave a duplicate-processing window when business effects and progress recording aren't atomic.

Further Reading

Written by Sam Erickson

Software engineer. I write every article on StackChief myself. Author of Mastering the Tech Interview. More about me