The Kafka Producer API
Explore how to use the Kafka Producer API to send messages to topics reliably and efficiently. Learn the differences between synchronous and asynchronous producers, configure key settings, and apply practical Java examples to build high-performance Kafka producers.
Introduction to Kafka producers
In Apache Kafka, producers are responsible for publishing messages (events) to Kafka topics. These messages can represent anything—from user clicks and system logs to sensor readings or financial transactions.
In Apache Kafka, producers are responsible for publishing messages (events) to Kafka topics. These messages can represent anything—from user clicks and system logs to sensor readings or financial transactions.
At a high level, sending data to Kafka using the Producer API involves just a few steps:
Configure the Kafka producer
Create a
KafkaProducerinstanceSend messages to a Kafka topic
While this sounds simple, how you send messages has a big impact on:
Performance
Reliability
Latency
Kafka supports two primary producer patterns:
Synchronous producers
Asynchronous producers
Let’s explore both and understand when to use each.
Producer configuration basics
Before sending messages, we configure the producer using a java.util.Properties object.
Common configuration properties include:
bootstrap.servers– Kafka broker addresskey.serializer– Converts keys to bytesvalue.serializer– Converts values to byteslinger.ms– Controls batching delay
These settings determine how efficiently and reliably messages are delivered to Kafka.
Tip: Producer configuration plays a major role in throughput and durability—especially in high-volume systems.
How synchronous producers work
In synchronous mode, the producer:
Sends a message to Kafka
Waits for a response from the broker
Continues only after receiving acknowledgment
This is done by calling .get() on the Future<RecordMetadata> returned by the send() method.
When to use it
Synchronous producers are useful when:
Every message must be confirmed
Failures must be handled immediately
Throughput is less important than correctness
The downside is lower performance, since each send blocks execution.
Synchronous producer example
Go through the example below and click the “Run” button to execute the code.
package com.example;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
public class KafkaProducerExample {
public static void main(String[] args) throws Exception {
String topicName = "sync-test-topic";
System.out.println("Executing synchronous Kafka producer");
Properties properties = new Properties();
properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
properties.setProperty(ProducerConfig.LINGER_MS_CONFIG, "1");
Producer<String, String> producer = new KafkaProducer<>(properties);
for (int i = 0; i < 5; i++) {
String key = "sync-key-" + i;
String value = "sync-value-" + i;
ProducerRecord<String, String> record = new ProducerRecord<>(topicName, key, value);
Future<RecordMetadata> response = producer.send(record);
response.get();
System.out.println("Produced data: " + key + "=" + value);
Thread.sleep(1000);
}
producer.flush();
producer.close();
System.out.println("End of program");
}
}
Code explanation
Let’s walk through important parts of the code and also understand the output of the program above:
Lines 3–9: We import packages from the standard library
Lines 11–16: We import packages from the Kafka library.
Line 21: We set the name of the Kafka topic to send messages to. In this case, it’s
sync-test-topic.Lines 25–29: We create a new
Propertiesobject to specify the configuration for the Kafka Producer.We set the bootstrap servers property to
localhost:9092, which specifies the location of the Kafka brokers.We set the key and value serializer properties to
org.apache.kafka.common.serialization.StringSerializer.We set the
linger.msproperty to1, which specifies how long the Kafka Producer waits for additional messages to batch together before sending them to the Kafka broker.
Line 31: We create a new
KafkaProducerinstance using the configuration properties.Lines 33–46: We start a loop that sends five messages to the Kafka topic. For each iteration, perform the following:
Generate a key and value for the message.
Create a new
ProducerRecordinstance with the topic name, key, and value.Send the record (key-value pair) to the Kafka broker using the
sendmethod. It returns aFutureobject representing the acknowledgment of the message from the broker.Call the
getmethod on theFutureobject to block until the message has been successfully sent to the broker.Print the key, and the value is successfully sent to the broker.
Wait for one second before sending the next message.
Line 49: We close the
KafkaProducerinstance.
How asynchronous producers work
In asynchronous mode, the producer:
Sends messages without waiting for Kafka’s response
Uses a callback function to handle success or failure
Continues sending messages immediately
This approach allows Kafka to batch messages efficiently and maximize throughput.
When to use it
Asynchronous producers are ideal when:
Performance and scalability matter
Occasional retries are acceptable
Systems process large volumes of events
Most production Kafka pipelines use asynchronous producers.
Asynchronous producer example
Go through the example below and click the “Run” button to execute the code.
package com.example;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
public class KafkaProducerExample {
public static void main(String[] args) throws Exception {
String topicName = "async-test-topic";
System.out.println("Executing asynchronous Kafka producer");
Properties properties = new Properties();
properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
properties.setProperty(ProducerConfig.LINGER_MS_CONFIG, "1");
Producer<String, String> producer = new KafkaProducer<>(properties);
CountDownLatch counter = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
String key = "async-key-" + i;
String value = "async-value-" + i;
ProducerRecord<String, String> record = new ProducerRecord<>(topicName, key, value);
producer.send(record, new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception e) {
if (e != null) {
System.out.println("Error sending data " + key + e.getMessage());
} else {
System.out.println("Produced data: " + key + "=" + value);
}
counter.countDown();
}
});
Thread.sleep(1000);
}
counter.await();
producer.flush();
producer.close();
System.out.println("End of program");
}
}Code explanation
Let’s walk through important parts of the code and also understand the output of the program above:
Lines 3–9: We import packages from the standard library.
Lines 11–16: We import packages from the Kafka library.
Line 21: We set the name of the Kafka topic to send messages to. In this case, it’s
async-test-topic.Lines 25–29: We create a new
Propertiesobject to specify the configuration for the Kafka Producer.We set the bootstrap servers property to
localhost:9092, which specifies the location of the Kafka brokers.We set the key and value serializer properties to
org.apache.kafka.common.serialization.StringSerializer.We set the
linger.msproperty to1, which specifies how long the Kafka Producer waits for additional messages to batch together before sending them to the Kafka broker.
Line 31: We create a new
KafkaProducerinstance using the configuration properties.Line 33: We create a
CountDownLatchobject with a count of5. This is used to wait until all messages have been sent before closing the producer.Lines 35–57: We start a loop that asynchronously sends
5messages to the Kafka topic. For each iteration, we perform the following:Generate a key and value for the message.
Create a new
ProducerRecordinstance with the topic name, key, and value.Use the
sendmethod with aCallbackobject to send the record to the Kafka broker. TheCallbackobject handles the acknowledgment of the message from the broker.In the
Callbackobject’sonCompletionmethod, check if an exception occurred while sending the message. If an exception occured, print an error message. Otherwise, print a message indicating the key and value of the message that was sent. Then, decrement the count of theCountDownLatchobject.Wait for one second before sending the next message.
Line 59: We call the
awaitmethod on theCountDownLatchobject to wait until all messages have been sent and acknowledged by the broker.Line 62: We close the
KafkaProducerinstance.
Note: A
CountDownLatchhas been used in this program for demonstration purposes so that we can see all the messages being sent successfully. Without this, the program would end without us being able to see the output. In a production application, this is typically not used when we want to send messages to Kafka asynchronously.
In this lesson, we covered synchronous and asynchronous Kafka producers and their differences, along with practical examples of how to use these with the Kafka Java client.