[{"data":1,"prerenderedAt":1160},["ShallowReactive",2],{"blog-post-\u002Fblog\u002Fkafka-java-clients":3},{"id":4,"title":5,"body":6,"date":1145,"description":1146,"extension":1147,"meta":1148,"navigation":1149,"ogImage":1150,"path":1151,"pinned":1152,"seo":1153,"sitemap":1154,"stem":1155,"tags":1156,"__hash__":1159},"blog\u002Fblog\u002Fkafka-java-clients.md","Kafka Java Clients and Kafka Streams: How to Choose",{"type":7,"value":8,"toc":1113},"minimark",[9,18,37,42,140,151,155,158,166,169,173,176,332,335,344,348,365,370,411,417,420,434,438,490,495,498,509,530,534,566,571,575,589,593,602,605,610,614,617,719,723,736,740,791,796,799,814,818,851,856,860,875,879,882,889,893,909,920,929,933,944,951,954,961,969,973,981,984,992,999,1003,1007,1024,1028,1037,1041,1053,1065,1069,1072,1075,1079,1096,1100,1103,1107,1110],[10,11,12,13,17],"p",{},"The Java Kafka libraries in this guide all build on Apache's official ",[14,15,16],"code",{},"kafka-clients"," library. What differs is how much polling, offset management, and recovery a framework handles, and whether the application needs basic message consumption or stateful stream processing with windows, joins, and recoverable aggregates.",[10,19,20,21,25,26,25,29,32,33,36],{},"This guide compares the ",[22,23,24],"strong",{},"Apache Kafka Java client",", ",[22,27,28],{},"Spring Kafka",[22,30,31],{},"Spring Cloud Stream",", and ",[22,34,35],{},"Kafka Streams",", alongside Kafka connectors for Akka and Pekko. The focus is on consumer models, retries, transactions, and serialization.",[38,39,41],"h2",{"id":40},"quick-decision","Quick decision",[43,44,45,61],"table",{},[46,47,48],"thead",{},[49,50,51,55,58],"tr",{},[52,53,54],"th",{},"Situation",[52,56,57],{},"Best starting point",[52,59,60],{},"Main caveat",[62,63,64,77,89,102,115,127],"tbody",{},[49,65,66,72,74],{},[67,68,69],"td",{},[22,70,71],{},"Spring Boot event publisher or consumer",[67,73,28],{},[67,75,76],{},"If your team already uses Spring Cloud Stream function bindings, use the Kafka binder row below.",[49,78,79,84,86],{},[67,80,81],{},[22,82,83],{},"Java service without Spring",[67,85,24],{},[67,87,88],{},"Your application manages polling, commits, and processing failures.",[49,90,91,96,99],{},[67,92,93],{},[22,94,95],{},"Team already using Spring Cloud Stream function bindings",[67,97,98],{},"Spring Cloud Stream Kafka binder",[67,100,101],{},"Partitioning, commits, and serialization still need Kafka-specific configuration.",[49,103,104,109,112],{},[67,105,106],{},[22,107,108],{},"Application built on Akka or Pekko Streams",[67,110,111],{},"Alpakka Kafka for Akka; Apache Pekko Kafka Connector for Pekko",[67,113,114],{},"Alpakka uses BSL 1.1; Pekko uses Apache-2.0.",[49,116,117,122,124],{},[67,118,119],{},[22,120,121],{},"Windows, joins, or recoverable aggregates",[67,123,35],{},[67,125,126],{},"Plan for local state storage, recovery time, and internal topics.",[49,128,129,134,137],{},[67,130,131],{},[22,132,133],{},"Kafka Streams with Spring Cloud Stream conventions",[67,135,136],{},"Spring Cloud Stream Kafka Streams binder",[67,138,139],{},"Adds function bindings, not additional window, join, or state-store capabilities.",[10,141,142,143,150],{},"A Spring Boot application can run Kafka Streams through ",[144,145,149],"a",{"href":146,"rel":147},"https:\u002F\u002Fdocs.spring.io\u002Fspring-kafka\u002Freference\u002Fstreams.html",[148],"nofollow","Spring Kafka's lifecycle integration",". Add the Spring Cloud Stream Kafka Streams binder when you want function bindings.",[38,152,154],{"id":153},"kafka-clients-or-kafka-streams","Kafka clients or Kafka Streams?",[10,156,157],{},"Use a messaging client when records trigger application work: send a notification, update an order, or call another service. Your application owns the business logic and any database state; the client or framework handles communication with Kafka.",[10,159,160,161,165],{},"Choose ",[144,162,35],{"href":163,"rel":164},"https:\u002F\u002Fkafka.apache.org\u002F41\u002Fstreams\u002Fcore-concepts\u002F",[148]," when you need managed state for operations such as five-minute sales totals, joins between orders and payments, or running inventory counts. It runs as a library inside your application. You define a topology—a graph of processing steps—and Kafka Streams manages the processing tasks and state recovery.",[10,167,168],{},"Alpakka and Pekko Kafka connectors belong with the messaging clients. They connect Kafka to an Akka or Pekko Streams pipeline so downstream stages can limit incoming work with backpressure. They do not provide Kafka Streams' managed state stores or recovery for windowed aggregates.",[38,170,172],{"id":171},"kafka-java-clients-compared","Kafka Java clients compared",[10,174,175],{},"Capabilities checked against official documentation on 7 Sep 2026; runtime requirements depend on the release line you select.",[43,177,178,194],{},[46,179,180],{},[49,181,182,185,187,189,191],{},[52,183,184],{},"Area",[52,186,24],{},[52,188,28],{},[52,190,98],{},[52,192,193],{},"Alpakka \u002F Pekko Kafka",[62,195,196,215,256,275,294,313],{},[49,197,198,203,206,209,212],{},[67,199,200],{},[22,201,202],{},"Framework dependency",[67,204,205],{},"None",[67,207,208],{},"Spring",[67,210,211],{},"Spring Boot and Spring Cloud Stream",[67,213,214],{},"Akka \u002F Pekko Streams",[49,216,217,222,229,235,245],{},[67,218,219],{},[22,220,221],{},"Consumer model",[67,223,224,225,228],{},"Explicit ",[14,226,227],{},"poll()"," loop",[67,230,231,234],{},[14,232,233],{},"@KafkaListener","; record or batch",[67,236,237,240,241,244],{},[14,238,239],{},"Consumer"," \u002F ",[14,242,243],{},"Function"," bindings",[67,246,247,25,250,32,253],{},[14,248,249],{},"Source",[14,251,252],{},"Flow",[14,254,255],{},"Sink",[49,257,258,263,266,269,272],{},[67,259,260],{},[22,261,262],{},"Concurrency",[67,264,265],{},"Application-managed consumers and workers",[67,267,268],{},"Concurrent listener containers",[67,270,271],{},"Binding concurrency",[67,273,274],{},"Stream parallelism and backpressure",[49,276,277,282,285,288,291],{},[67,278,279],{},[22,280,281],{},"Offset commits",[67,283,284],{},"Auto-commit or explicit commit APIs",[67,286,287],{},"Container acknowledgment modes",[67,289,290],{},"Binder acknowledgment configuration",[67,292,293],{},"Committable offsets and commit stages",[49,295,296,301,304,307,310],{},[67,297,298],{},[22,299,300],{},"Retries and dead letters",[67,302,303],{},"Application implements recovery",[67,305,306],{},"Error handlers and retry topics",[67,308,309],{},"Configurable retries and dead-letter routing",[67,311,312],{},"Stream recovery; custom dead-letter flow",[49,314,315,320,323,326,329],{},[67,316,317],{},[22,318,319],{},"Kafka transactions",[67,321,322],{},"Explicit producer transaction APIs",[67,324,325],{},"Transaction managers and containers",[67,327,328],{},"Transactional binder configuration",[67,330,331],{},"Transactional stream APIs",[10,333,334],{},"A dead-letter topic (DLT) holds records routed away after processing fails.",[10,336,337,338,343],{},"All these options can use Schema Registry by adding serializer dependencies and registry configuration. For example, ",[144,339,342],{"href":340,"rel":341},"https:\u002F\u002Fdocs.confluent.io\u002Fplatform\u002Fcurrent\u002Fclients\u002Fapp-development.html",[148],"Confluent's Java serializers"," add Avro, Protobuf, and JSON Schema support.",[345,346,24],"h3",{"id":347},"apache-kafka-java-client",[10,349,350,351,354,355,25,358,32,361,364],{},"The official ",[14,352,353],{},"org.apache.kafka:kafka-clients"," artifact provides ",[14,356,357],{},"KafkaProducer",[14,359,360],{},"KafkaConsumer",[14,362,363],{},"Admin",". It gives your application direct access to Kafka configuration, partition assignment, offsets, and transactions.",[10,366,367],{},[22,368,369],{},"Capabilities and limitations",[371,372,373,393,399],"ul",{},[374,375,376,379,380,382,383,386,387,392],"li",{},[22,377,378],{},"Consumer ownership:"," Your code runs the polling loop and coordinates processing with commits. ",[14,381,360],{}," is not thread-safe; moving work to an executor requires tracking completion before committing offsets. Long processing can exceed ",[14,384,385],{},"max.poll.interval.ms"," and trigger reassignment. See the ",[144,388,391],{"href":389,"rel":390},"https:\u002F\u002Fkafka.apache.org\u002F41\u002Fjavadoc\u002Forg\u002Fapache\u002Fkafka\u002Fclients\u002Fconsumer\u002FKafkaConsumer.html",[148],"consumer API",".",[374,394,395,398],{},[22,396,397],{},"Recovery:"," Client retries handle communication failures. Retrying business logic, publishing failed records to a DLT, and deciding when to skip a record remain application responsibilities.",[374,400,401,404,405,410],{},[22,402,403],{},"Runtime:"," Kafka 4.0 raised the minimum Java version for clients and Streams to 11. Broker requirements are different, as the ",[144,406,409],{"href":407,"rel":408},"https:\u002F\u002Fkafka.apache.org\u002F41\u002Fgetting-started\u002Fcompatibility\u002F",[148],"compatibility matrix"," shows; check the Java requirement for your client release separately.",[10,412,413,416],{},[22,414,415],{},"When to choose:"," Use the Apache client for a service without Spring, a reusable integration library, or a consumer that needs custom polling and commit behavior. Choose Spring Kafka if you would otherwise build listener management and recovery infrastructure inside a Spring application.",[345,418,28],{"id":419},"spring-kafka",[10,421,422,426,427,430,431,433],{},[144,423,28],{"href":424,"rel":425},"https:\u002F\u002Fdocs.spring.io\u002Fspring-kafka\u002Freference\u002F",[148]," adds ",[14,428,429],{},"KafkaTemplate"," for sending and listener containers for consuming. With ",[14,432,233],{},", the framework handles polling and invokes your method with a record or batch.",[10,435,436],{},[22,437,369],{},[371,439,440,467,478],{},[374,441,442,445,446,449,450,453,454,461,462,392],{},[22,443,444],{},"Recovery tools:"," ",[14,447,448],{},"DefaultErrorHandler"," configures retries, and ",[14,451,452],{},"DeadLetterPublishingRecoverer"," can route failed records to a DLT. Use ",[144,455,458],{"href":456,"rel":457},"https:\u002F\u002Fdocs.spring.io\u002Fspring-kafka\u002Freference\u002Fkafka\u002Fserdes.html",[148],[14,459,460],{},"ErrorHandlingDeserializer"," to pass deserialization failures to the container's error handler. Transactional containers use rollback processing. See ",[144,463,466],{"href":464,"rel":465},"https:\u002F\u002Fdocs.spring.io\u002Fspring-kafka\u002Freference\u002Fkafka\u002Fannotation-error-handling.html",[148],"exception handling",[374,468,469,445,472,477],{},[22,470,471],{},"Retry trade-offs:",[144,473,476],{"href":474,"rel":475},"https:\u002F\u002Fdocs.spring.io\u002Fspring-kafka\u002Freference\u002Fretrytopic.html",[148],"Non-blocking retries"," move records through retry topics, allowing later records to proceed. They do not preserve the original processing order and cannot be combined with container transactions or batch listeners.",[374,479,480,483,484,489],{},[22,481,482],{},"Version alignment:"," The current ",[144,485,488],{"href":486,"rel":487},"https:\u002F\u002Fdocs.spring.io\u002Fspring-kafka\u002Freference\u002Fquick-tour.html",[148],"quick tour"," specifies Java 17 as the minimum. In Spring Boot projects, use Boot's dependency management to select compatible Spring Kafka and Kafka client versions.",[10,491,492,494],{},[22,493,415],{}," Choose Spring Kafka for Spring services that need explicit control over listener concurrency, acknowledgment, and failure recovery. Its container configuration is usually a better fit than Spring Cloud Stream when Kafka-specific behavior is central to the application.",[345,496,98],{"id":497},"spring-cloud-stream-kafka-binder",[10,499,500,501,504,505,508],{},"Spring Cloud Stream connects application functions to messaging systems through adapters called binders. Its ",[22,502,503],{},"Kafka binder"," handles messaging; its ",[22,506,507],{},"Kafka Streams binder"," runs Kafka Streams topologies. They are separate dependencies with different programming and configuration models.",[10,510,511,512,515,516,25,519,521,522,524,525,392],{},"With ",[14,513,514],{},"spring-cloud-stream-binder-kafka",", you define ",[14,517,518],{},"Supplier",[14,520,239],{},", or ",[14,523,243],{}," beans and configure their destinations and consumer groups. The binder uses Spring Kafka underneath. See the ",[144,526,529],{"href":527,"rel":528},"https:\u002F\u002Fdocs.spring.io\u002Fspring-cloud-stream\u002Freference\u002Fspring-cloud-stream\u002Fproducing-and-consuming-messages.html",[148],"functional programming model",[10,531,532],{},[22,533,369],{},[371,535,536,542,560],{},[374,537,538,541],{},[22,539,540],{},"Configuration:"," Bindings centralize topics, groups, concurrency, and message conversion. Kafka-specific behavior still needs Kafka-specific settings; changing binders does not make partitioning or delivery semantics interchangeable.",[374,543,544,547,548,553,554,559],{},[22,545,546],{},"Failures and transactions:"," The binder offers ",[144,549,552],{"href":550,"rel":551},"https:\u002F\u002Fdocs.spring.io\u002Fspring-cloud-stream\u002Freference\u002Fkafka\u002Fkafka-binder\u002Fretry-dlq.html",[148],"retry and dead-letter configuration",". With the ",[144,555,558],{"href":556,"rel":557},"https:\u002F\u002Fdocs.spring.io\u002Fspring-cloud-stream\u002Freference\u002Fkafka\u002Fkafka-binder\u002Ftransactional.html",[148],"transactional binder",", retry and recovery use the listener container's rollback handling instead of ordinary binder retries.",[374,561,562,565],{},[22,563,564],{},"Serialization:"," Decide whether Spring message conversion or Kafka's native serializers own the payload format. For an existing Schema Registry contract, use matching native serializers and deserializers rather than assuming JSON conversion produces the same bytes.",[10,567,568,570],{},[22,569,415],{}," Choose the Kafka binder when your team already organizes services around Spring Cloud Stream function bindings. Choose Spring Kafka when direct listener configuration is clearer than another configuration layer.",[345,572,574],{"id":573},"alpakka-kafka-and-apache-pekko-kafka-connector","Alpakka Kafka and Apache Pekko Kafka Connector",[10,576,577,582,583,588],{},[144,578,581],{"href":579,"rel":580},"https:\u002F\u002Fdoc.akka.io\u002Flibraries\u002Falpakka-kafka\u002Fcurrent\u002Fhome.html",[148],"Alpakka Kafka"," integrates with Akka Streams; ",[144,584,587],{"href":585,"rel":586},"https:\u002F\u002Fpekko.apache.org\u002Fdocs\u002Fpekko-connectors-kafka\u002Fcurrent\u002Fhome.html",[148],"Apache Pekko Kafka Connector"," provides the corresponding integration for Pekko Streams. Both offer Java APIs for composing Kafka records with asynchronous processing stages.",[10,590,591],{},[22,592,369],{},[10,594,595,596,601],{},"Committable sources let processing carry an offset through the pipeline and commit after work completes. Transactional APIs support Kafka-to-Kafka processing; Pekko documents this in its ",[144,597,600],{"href":598,"rel":599},"https:\u002F\u002Fpekko.apache.org\u002Fdocs\u002Fpekko-connectors-kafka\u002Fcurrent\u002Ftransactions.html",[148],"transaction guide",". Application failures still need a recovery policy, including what happens to unfinished records when a stream stops.",[10,603,604],{},"Neither requires writing business logic as actors, but both introduce their stream runtime and dependencies.",[10,606,607,609],{},[22,608,415],{}," Use the connector matching an existing Akka or Pekko application. For a new pipeline where Apache-2.0 licensing is required, consider Pekko; current Alpakka Kafka releases use Business Source License 1.1. Adding an Akka or Pekko Streams runtime solely for a straightforward Kafka listener is usually unnecessary.",[38,611,613],{"id":612},"kafka-streams-directly-or-with-the-spring-cloud-stream-binder","Kafka Streams: directly or with the Spring Cloud Stream binder",[10,615,616],{},"The Spring Cloud Stream Kafka Streams binder adds function bindings, configuration, and lifecycle management around Kafka Streams. Both approaches use the same processing engine. Serialization uses Serdes, which pair serializers with deserializers for record types.",[43,618,619,630],{},[46,620,621],{},[49,622,623,625,628],{},[52,624,184],{},[52,626,627],{},"Kafka Streams directly",[52,629,136],{},[62,631,632,654,667,680,693,706],{},[49,633,634,639,642],{},[67,635,636],{},[22,637,638],{},"Programming model",[67,640,641],{},"Build a topology with the DSL or Processor API",[67,643,644,645,25,648,521,651],{},"Functions using ",[14,646,647],{},"KStream",[14,649,650],{},"KTable",[14,652,653],{},"GlobalKTable",[49,655,656,661,664],{},[67,657,658],{},[22,659,660],{},"Configuration",[67,662,663],{},"Kafka Streams properties and explicit topology setup",[67,665,666],{},"Bindings plus Kafka Streams properties",[49,668,669,674,677],{},[67,670,671],{},[22,672,673],{},"State and joins",[67,675,676],{},"Kafka Streams state stores, windows, and joins",[67,678,679],{},"Same Kafka Streams capabilities",[49,681,682,687,690],{},[67,683,684],{},[22,685,686],{},"Serialization",[67,688,689],{},"Explicit or default Serdes",[67,691,692],{},"Type-based inference or configured Serdes",[49,694,695,700,703],{},[67,696,697],{},[22,698,699],{},"Error handling",[67,701,702],{},"Kafka Streams exception handlers",[67,704,705],{},"Binder configuration, including deserialization DLQ routing",[49,707,708,713,716],{},[67,709,710],{},[22,711,712],{},"Lifecycle",[67,714,715],{},"Application-managed; Spring Kafka integration also available",[67,717,718],{},"Binder-managed within Spring Boot",[345,720,722],{"id":721},"apache-kafka-streams","Apache Kafka Streams",[10,724,725,726,729,730,732,733,735],{},"The ",[14,727,728],{},"org.apache.kafka:kafka-streams"," library provides a DSL for filtering, grouping, aggregating, and joining records, plus a Processor API for custom processing. A ",[14,731,647],{}," represents events; a ",[14,734,650],{}," represents updates to values by key.",[10,737,738],{},[22,739,369],{},[371,741,742,752,763,778],{},[374,743,744,747,748,392],{},[22,745,746],{},"State and recovery:"," Persistent state stores commonly use RocksDB locally, with Kafka changelog topics supporting recovery. Stateful applications need disk capacity and time to restore state after reassignment. Some operations also create repartition topics to redistribute records by key. See the ",[144,749,751],{"href":163,"rel":750},[148],"core concepts",[374,753,754,445,757,762],{},[22,755,756],{},"Querying state:",[144,758,761],{"href":759,"rel":760},"https:\u002F\u002Fkafka.apache.org\u002F41\u002Fstreams\u002Fdeveloper-guide\u002Finteractive-queries\u002F",[148],"Interactive Queries"," provides read-only access to local state stores. Queries for state held by another application instance require application-provided routing and network calls.",[374,764,765,768,769,772,773,392],{},[22,766,767],{},"Processing guarantees:"," Setting ",[14,770,771],{},"processing.guarantee=exactly_once_v2"," coordinates input offsets, state updates, and Kafka output records. It does not make an external database write or HTTP request exactly-once. The default is at-least-once. See ",[144,774,777],{"href":775,"rel":776},"https:\u002F\u002Fkafka.apache.org\u002F41\u002Fstreams\u002Fdeveloper-guide\u002Fconfig-streams\u002F",[148],"Streams configuration",[374,779,780,445,783,790],{},[22,781,782],{},"Testing:",[144,784,787],{"href":785,"rel":786},"https:\u002F\u002Fkafka.apache.org\u002F41\u002Fstreams\u002Fdeveloper-guide\u002Ftesting\u002F",[148],[14,788,789],{},"TopologyTestDriver"," tests transformations and state with controlled inputs without a broker. Use broker integration tests for deployment behavior such as authentication and recovery after process failure.",[10,792,793,795],{},[22,794,415],{}," Use Kafka Streams directly when you want explicit topology configuration without Spring Cloud Stream function bindings.",[345,797,136],{"id":798},"spring-cloud-stream-kafka-streams-binder",[10,800,725,801,804,805,808,809,392],{},[14,802,803],{},"spring-cloud-stream-binder-kafka-streams"," artifact connects functions such as ",[14,806,807],{},"Function\u003CKStream\u003CK, V>, KStream\u003CK, R>>"," to Kafka topics. See its ",[144,810,813],{"href":811,"rel":812},"https:\u002F\u002Fdocs.spring.io\u002Fspring-cloud-stream\u002Freference\u002Fkafka\u002Fkafka-streams-binder\u002Fprogramming-model.html",[148],"programming model",[10,815,816],{},[22,817,369],{},[371,819,820,826,838],{},[374,821,822,825],{},[22,823,824],{},"Configuration remains important:"," Set a stable application ID for each topology and configure Serdes for its record types. The binder does not remove Kafka Streams' internal topics, state storage, or partitioning requirements.",[374,827,828,831,832,837],{},[22,829,830],{},"Error handling:"," The binder can ",[144,833,836],{"href":834,"rel":835},"https:\u002F\u002Fdocs.spring.io\u002Fspring-cloud-stream\u002Freference\u002Fkafka\u002Fkafka-streams-binder\u002Ferror-handling.html",[148],"send records that fail deserialization to a dead-letter topic",". This does not mean every exception in a processor is automatically retried or sent there.",[374,839,840,843,844,850],{},[22,841,842],{},"Spring without a binder:"," Spring Kafka already provides ",[144,845,847],{"href":146,"rel":846},[148],[14,848,849],{},"StreamsBuilderFactoryBean"," to manage Kafka Streams within a Spring application. You do not need Spring Cloud Stream solely to start and stop a topology with Spring Boot.",[10,852,853,855],{},[22,854,415],{}," Use this binder when your team already uses Spring Cloud Stream function bindings. Otherwise, use Kafka Streams directly, with Spring Kafka lifecycle integration if needed.",[345,857,859],{"id":858},"when-to-consider-flink-or-spark","When to consider Flink or Spark",[10,861,862,863,868,869,874],{},"Consider ",[144,864,867],{"href":865,"rel":866},"https:\u002F\u002Fnightlies.apache.org\u002Fflink\u002Fflink-docs-stable\u002Fdocs\u002Fconnectors\u002Fdatastream\u002Fkafka\u002F",[148],"Flink"," when the team wants streaming jobs managed by a dedicated processing runtime, particularly pipelines combining Kafka with other sources and sinks. Consider ",[144,870,873],{"href":871,"rel":872},"https:\u002F\u002Fspark.apache.org\u002Fdocs\u002Flatest\u002Fstructured-streaming-kafka-integration.html",[148],"Spark Structured Streaming"," when the workload fits an existing Spark SQL or DataFrame pipeline. Both introduce a separate processing runtime to deploy and operate; Kafka Streams fits when the processing should run as part of a Java service.",[38,876,878],{"id":877},"test-java-kafka-clients-with-kafma","Test Java Kafka clients with Kafma",[10,880,881],{},"Spring message conversion, native serializers, and type headers can change what a consumer receives. Retry and dead-letter settings determine what happens when processing fails.",[10,883,884,888],{},[144,885,887],{"href":886},"\u002F","Kafma"," is a desktop Kafka UI that runs alongside your Java application. Use it to inspect records from your producer or Kafka Streams output topic, then send test records to your running consumer.",[345,890,892],{"id":891},"inspect-serialized-records-schemas-and-spring-headers","Inspect serialized records, schemas, and Spring headers",[10,894,895,896,900,901,904,905,908],{},"Send a record from your application to a test topic, or open your Kafka Streams output topic in Kafma. Inspect the key, value, and headers. With Schema Registry configured, Kafma ",[144,897,899],{"href":898},"\u002Fdocs\u002Fconsole\u002Fdecoding-messages","automatically decodes"," records using the standard Confluent schema-ID payload format. Check the schema ID and switch between ",[22,902,903],{},"Decoded"," and ",[22,906,907],{},"Raw"," to compare the readable value with its bytes.",[10,910,911,912,915,916,392],{},"For Spring JSON messages, compare ",[14,913,914],{},"__TypeId__"," and related headers with your consumer's target type or type mappings. Missing headers are not necessarily an error: the deserializer can use a configured type and ignore headers. See ",[144,917,919],{"href":456,"rel":918},[148],"Spring's serialization options",[10,921,922],{},[923,924],"img",{"alt":925,"height":926,"src":927,"width":928},"Kafma displaying a JSON order record with its Spring TypeId header, key, and metadata",1520,"https:\u002F\u002Fmedia.kafma.app\u002Fblog\u002Fkafka-java-clients\u002Fspring-json-type-header.png",3024,[345,930,932],{"id":931},"send-test-messages-to-your-consumer","Send test messages to your consumer",[10,934,935,936,938,939,943],{},"Keep your consumer running—for example, the service containing your ",[14,937,233],{},"—and use Kafma's ",[144,940,942],{"href":941},"\u002Fdocs\u002Fconsole\u002Fproducing-messages","producer panel"," to send a valid record to its test topic. Then send one with a payload or type header that does not match your consumer configuration, followed by another valid record. Send all three records to the same partition.",[10,945,946,947,950],{},"To test invalid JSON, select ",[22,948,949],{},"String"," encoding for the value and send malformed JSON text.",[10,952,953],{},"Check your application logs and configured dead-letter topic. Is the failed record retried, routed to that topic, or stopping processing? Does the next valid record get processed? Compare the result with your error-handling configuration; a deserialization failure may occur before your listener runs.",[10,955,956,957,960],{},"For a stop-and-restart test with messages arriving, use ",[22,958,959],{},"Loop"," to send records at a fixed interval and observe how your consumer handles unfinished work.",[10,962,963],{},[923,964],{"alt":965,"height":966,"src":967,"width":968},"Kafma producing schema-generated records on a timed loop while Console shows incoming messages",1366,"https:\u002F\u002Fmedia.kafma.app\u002Fchangelog\u002Fv1.0.0\u002Fhomepage-console-produce.png",2428,[345,970,972],{"id":971},"clone-data-for-testing","Clone data for testing",[10,974,975,976,980],{},"Hand-written test data can miss older schema versions, missing headers, and tombstones with null values. ",[144,977,979],{"href":978},"\u002Ffeatures\u002Fkafka-data-clone","Kafma Data Clone"," copies a selected message range to a test topic, with optional topic creation, schema registration, and field masking. Cross-cluster cloning requires Pro.",[10,982,983],{},"Use the copy to test your consumer's deserialization and message handling. Copied records receive new offsets and may use different partitions; consumer-group offsets are not copied.",[10,985,986],{},[923,987],{"alt":988,"height":989,"src":990,"width":991},"Kafma cloning selected topics and masked records into a local cluster",1564,"https:\u002F\u002Fmedia.kafma.app\u002Fchangelog\u002Fv1.0.0\u002Fhomepage-data-clone.png",2740,[10,993,994,998],{},[144,995,997],{"href":996},"\u002Fdownload","Download Kafma"," and connect to the same test cluster as your Java application.",[38,1000,1002],{"id":1001},"frequently-asked-questions","Frequently asked questions",[345,1004,1006],{"id":1005},"does-confluent-provide-its-own-java-kafka-client","Does Confluent provide its own Java Kafka client?",[10,1008,1009,1010,1015,1016,904,1018,1020,1021,1023],{},"Confluent's ",[144,1011,1014],{"href":1012,"rel":1013},"https:\u002F\u002Fdocs.confluent.io\u002Fkafka-clients\u002Fjava\u002Fcurrent\u002Foverview.html",[148],"Java client documentation"," uses Apache's ",[14,1017,357],{},[14,1019,360],{},". For standard Java messaging, you use ",[14,1022,353],{},"; Confluent adds components such as Schema Registry clients and Avro, Protobuf, and JSON Schema serializers. You do not need a separate Confluent producer\u002Fconsumer API to connect to Confluent Cloud.",[345,1025,1027],{"id":1026},"is-reactor-kafka-still-maintained","Is Reactor Kafka still maintained?",[10,1029,1030,1031,1036],{},"Spring ",[144,1032,1035],{"href":1033,"rel":1034},"https:\u002F\u002Fspring.io\u002Fblog\u002F2025\u002F05\u002F20\u002Freactor-kafka-discontinued\u002F",[148],"announced the discontinuation of Reactor Kafka in May 2025",". For a new Spring service, start with Spring Kafka. Teams maintaining Reactor Kafka applications should evaluate migrating to Spring Kafka and review processing completion, backpressure, and commits when changing APIs. Using WebFlux for HTTP endpoints does not by itself require a reactive Kafka client.",[345,1038,1040],{"id":1039},"which-java-kafka-clients-support-the-kip-848-consumer-group-protocol","Which Java Kafka clients support the KIP-848 consumer group protocol?",[10,1042,1043,1044,1047,1048,392],{},"Apache Kafka's Java consumer supports KIP-848, generally available with Kafka 4.0. Enable it with ",[14,1045,1046],{},"group.protocol=consumer"," on compatible clients and brokers. ",[144,1049,1052],{"href":1050,"rel":1051},"https:\u002F\u002Fdocs.spring.io\u002Fspring-kafka\u002Freference\u002Fkafka\u002Freceiving-messages\u002Frebalance-listeners.html",[148],"Spring Kafka supports it from version 4.0 onward",[10,1054,1055,1056,1058,1059,1064],{},"For Spring Cloud Stream and Akka\u002FPekko connectors, check both the resolved Kafka client version and the framework's compatibility guidance. Depending on ",[14,1057,16],{}," alone is not enough to establish that every wrapper feature works with the new protocol. Assignment and heartbeat settings also change; use Apache's ",[144,1060,1063],{"href":1061,"rel":1062},"https:\u002F\u002Fkafka.apache.org\u002F41\u002Foperations\u002Fconsumer-rebalance-protocol\u002F",[148],"upgrade guidance"," when enabling it.",[345,1066,1068],{"id":1067},"can-producers-and-consumers-use-different-java-kafka-libraries","Can producers and consumers use different Java Kafka libraries?",[10,1070,1071],{},"Yes. They must agree on key and value serialization, required headers, and Schema Registry framing. A Spring JSON consumer must resolve type headers to the intended Java class, or ignore them and use a configured target type.",[10,1073,1074],{},"Consumers sharing a group also need compatible group protocols and assignment behavior.",[345,1076,1078],{"id":1077},"can-spring-kafka-and-kafka-streams-run-in-the-same-application","Can Spring Kafka and Kafka Streams run in the same application?",[10,1080,1081,1082,25,1084,1086,1087,1091,1092,1095],{},"Yes. A Spring Boot application can use ",[14,1083,429],{},[14,1085,233],{},", and a Kafka Streams topology together. ",[144,1088,1090],{"href":146,"rel":1089},[148],"Spring Kafka manages the topology lifecycle","; Spring Cloud Stream is optional. Use a Streams ",[14,1093,1094],{},"application.id"," distinct from your listener group IDs.",[345,1097,1099],{"id":1098},"does-kafka-streams-require-a-separate-cluster","Does Kafka Streams require a separate cluster?",[10,1101,1102],{},"It needs a Kafka cluster, but no separate stream-processing cluster. The library runs inside your application, and multiple application instances can share processing tasks.",[38,1104,1106],{"id":1105},"conclusion","Conclusion",[10,1108,1109],{},"For most new Java services, start with Spring Kafka on Spring Boot, or the Apache Kafka Java client when you want direct control without a messaging framework. Choose Kafka Streams for windows, joins, or recoverable state; Spring Kafka can manage its lifecycle without Spring Cloud Stream. Use Spring Cloud Stream when your application already uses function bindings, or an Akka\u002FPekko connector when it already uses the corresponding Streams runtime.",[10,1111,1112],{},"This guide is maintained by the team behind Kafma.",{"title":1114,"searchDepth":1115,"depth":1115,"links":1116},"",3,[1117,1119,1120,1126,1131,1136,1144],{"id":40,"depth":1118,"text":41},2,{"id":153,"depth":1118,"text":154},{"id":171,"depth":1118,"text":172,"children":1121},[1122,1123,1124,1125],{"id":347,"depth":1115,"text":24},{"id":419,"depth":1115,"text":28},{"id":497,"depth":1115,"text":98},{"id":573,"depth":1115,"text":574},{"id":612,"depth":1118,"text":613,"children":1127},[1128,1129,1130],{"id":721,"depth":1115,"text":722},{"id":798,"depth":1115,"text":136},{"id":858,"depth":1115,"text":859},{"id":877,"depth":1118,"text":878,"children":1132},[1133,1134,1135],{"id":891,"depth":1115,"text":892},{"id":931,"depth":1115,"text":932},{"id":971,"depth":1115,"text":972},{"id":1001,"depth":1118,"text":1002,"children":1137},[1138,1139,1140,1141,1142,1143],{"id":1005,"depth":1115,"text":1006},{"id":1026,"depth":1115,"text":1027},{"id":1039,"depth":1115,"text":1040},{"id":1067,"depth":1115,"text":1068},{"id":1077,"depth":1115,"text":1078},{"id":1098,"depth":1115,"text":1099},{"id":1105,"depth":1118,"text":1106},"2026-09-07","Compare Spring Kafka, the Apache Kafka Java client, Kafka Streams, and Spring Cloud Stream — with recommendations by use case.","md",{},true,null,"\u002Fblog\u002Fkafka-java-clients",false,{"title":5,"description":1146},{"loc":1151},"blog\u002Fkafka-java-clients",[1157,35,1158],"Kafka Client Libraries","Java","lO7Aj3N9VWxckMNlVWptm2zfj6fTK1pmf-XSBkHafMU",1788772211352]