The Kafka producer is the client library responsible for publishing records to Kafka topics.
The producer handles serialization, partitioning, batching, and network communication with Kafka brokers.
Producer internals from application call to broker Producer internals from application call to broker Kafka Producer Serializers Record Accumulator Kafka Cluster Application Partitioner Sender Thread Network Client Key Serializer Value Serializer Batch (partition 0) Batch (partition 1) Batch (partition 2) Broker 1 Broker 2 Broker 3 Batches accumulate until: - batch.size reached - linger.ms elapsed key value assign partition batches ready
Component Responsibility Serializers Convert keys and values to bytes Partitioner Determine target partition for each record Record Accumulator Buffer records into batches per partition Sender Thread Background thread that transmits batches to brokers Metadata Cache of cluster topology and partition leadership
Properties props = new Properties () ;
props . put ( " bootstrap.servers " , " kafka1:9092,kafka2:9092,kafka3:9092 " ) ;
props . put ( " key.serializer " , " org.apache.kafka.common.serialization.StringSerializer " ) ;
props . put ( " value.serializer " , " org.apache.kafka.common.serialization.StringSerializer " ) ;
props . put ( " acks " , " all " ) ;
try ( Producer < String , String > producer = new KafkaProducer <>(props)) {
ProducerRecord < String , String > record = new ProducerRecord <>(
" { \" amount \" : 100} " // value
RecordMetadata metadata = producer . send ( record ) . get () ;
System . out . printf ( " Sent to partition %d offset %d%n " ,
metadata . partition () , metadata . offset ()) ;
// Asynchronous send with callback
producer . send ( record, (meta, exception) -> {
exception . printStackTrace () ;
System . out . printf ( " Sent to partition %d offset %d%n " ,
meta . partition () , meta . offset ()) ;
from kafka import KafkaProducer
producer = KafkaProducer (
bootstrap_servers = [ ' kafka1:9092 ' , ' kafka2:9092 ' , ' kafka3:9092 ' ],
key_serializer =lambda k : k. encode ( ' utf-8 ' ) ,
value_serializer =lambda v : json. dumps ( v ) . encode ( ' utf-8 ' ) ,
# Block for synchronous send
record_metadata = future. get ( timeout = 10 )
print ( f "Sent to partition {record_metadata.partition} offset {record_metadata.offset} " )
Configuration Default Description bootstrap.servers- Comma-separated list of broker addresses key.serializer- Serializer class for record keys value.serializer- Serializer class for record values acksall (3.0+)Acknowledgment level: 0, 1, or all retries2147483647 Number of retry attempts delivery.timeout.ms120000 Upper bound on time to report success/failure
Configuration Default Description batch.size16384 Maximum batch size in bytes linger.ms0 Time to wait for additional records buffer.memory33554432 Total memory for buffering max.block.ms60000 Time to block when buffer is full
Configuration Default Description enable.idempotencetrue (3.0+) Enable idempotent producer max.in.flight.requests.per.connection5 Max unacknowledged requests per connection transactional.idnull Transaction ID for transactional producer
Configuration Default Description compression.typenone Compression: none, gzip, snappy, lz4, zstd max.request.size1048576 Maximum request size in bytes send.buffer.bytes131072 TCP send buffer size receive.buffer.bytes32768 TCP receive buffer size
→ Configuration Reference
The acks configuration determines how many replicas must acknowledge a write before the producer considers it successful.
Acknowledgment behavior for acks=0, acks=1, and acks=all Producer Leader Follower 1 Follower 2 Producer Producer Leader Leader Follower 1 Follower 1 Follower 2 Follower 2 acks=0 (Fire and Forget) send No wait for ack acks=1 (Leader Only) send ack Leader writes, acks immediately acks=all (All ISR) send replicate replicate ack ack ack Wait for all ISR
acks Durability Latency Use Case 0Lowest—may lose data Lowest Metrics, logs where loss acceptable 1Medium—loses data if leader fails before replication Medium Low latency, some durability allHighest—survives broker failures Highest Production default for important data
acks=all Requires min.insync.replicas
With acks=all, the producer waits for all in-sync replicas. If min.insync.replicas is not set, this could mean waiting for only the leader if followers fall out of sync.
The partitioner determines which partition receives each record. Partitioning affects ordering, parallelism, and load distribution.
Default partitioner decision path for keyed and null-key records Default partitioner decision path for keyed and null-key records Key is null? yes no Kafka 2.4+? yes no Batch to single partition until batch is full Sticky Partitioner Round-robin Same key always goes to same partition hash(key) mod partitions
Key Behavior Use Case With key Consistent hashing—same key → same partition Ordering by entity (user, order) Null key (2.4+) Sticky partitioning—fills batch before switching High throughput, no ordering needed Null key (pre-2.4) Round-robin Legacy behavior
public class RegionPartitioner implements Partitioner {
public int partition ( String topic , Object key , byte [] keyBytes ,
Object value , byte [] valueBytes , Cluster cluster ) {
String region = extractRegion ( value ) ;
int numPartitions = cluster . partitionCountForTopic ( topic ) ;
// Partition 0-2 for US, 3-5 for EU, 6-8 for APAC
case " US " : return key . hashCode () % 3 ;
case " EU " : return 3 + ( key . hashCode () % 3 );
case " APAC " : return 6 + ( key . hashCode () % 3 );
default: return Math . abs ( key . hashCode () % numPartitions ) ;
Consideration Guidance Ordering requirements Use consistent keys for records that must be ordered together Hot partitions Avoid keys that create uneven distribution Partition count More partitions enable more parallelism but add overhead Consumer parallelism Maximum consumer parallelism equals partition count
→ Partitioning Guide
Batching improves throughput by amortizing network and disk I/O overhead across multiple records.
Record accumulator batches released on batch.size or linger.ms Record accumulator batches released on batch.size or linger.ms Record Accumulator Partition 0 Queue Partition 1 Queue Partition 2 Queue Batch (filling) Batch (full) Batch (filling) Batch (ready: linger.ms) Sender Thread batch.size = 16KB linger.ms = 5ms batch.size reached linger.ms elapsed
Configuration Effect of Increasing batch.sizeLarger batches, more compression, higher latency linger.msMore time to fill batches, higher latency buffer.memoryMore batches can accumulate, handles bursts better
Workload batch.size linger.ms Rationale Low latency 16384 0-5 Send immediately or with minimal wait High throughput 65536+ 10-100 Fill larger batches for efficiency Balanced 32768 5-20 Good throughput with acceptable latency
→ Batching Guide
Compression reduces network bandwidth and storage at the cost of CPU.
Type Compression Ratio CPU Cost Speed none1.0x None Fastest gzipBest (0.3-0.5x) High Slowest snappyGood (0.5-0.7x) Low Fast lz4Good (0.5-0.7x) Low Fastest zstdBetter (0.4-0.6x) Medium Fast
End-to-end compression from producer through broker to consumer End-to-end compression from producer through broker to consumer Producer Broker Consumer Batch Compress (if configured) Store (compressed) Decompress Process Broker stores data in producer's compression format (end-to-end) raw batch compressed batch compressed batch raw records
Scenario Recommended Rationale Network-constrained gzip or zstdMaximum compression CPU-constrained lz4 or snappyLow CPU overhead Balanced zstdGood ratio with reasonable CPU Latency-sensitive lz4Fastest compression/decompression
→ Compression Guide
Idempotent producers prevent duplicate messages during retries by assigning sequence numbers to each record.
Idempotent producer deduplicating a retried batch by sequence number Producer Broker Producer (PID: 1000) Producer (PID: 1000) Broker Broker send(seq=0, batch=[A,B,C]) ack(seq=0) send(seq=1, batch=[D,E]) Network timeout, no ack received retry send(seq=1, batch=[D,E]) seq=1 already processed ack(seq=1, deduplicated) Broker tracks (PID, partition, seq) Duplicate sequences are ignored
# Enable idempotent producer (default in Kafka 3.0+)
# Implied settings when idempotence is enabled:
# max.in.flight.requests.per.connection <= 5
Scope Covered Single session Duplicates prevented within producer session Across restarts Duplicates possible—use transactions for exactly-once Across partitions Each partition tracked independently
→ Idempotence Guide
Transactions enable atomic writes to multiple partitions and exactly-once semantics when combined with transactional consumers.
Transactional producer flow from initTransactions to commit markers Producer Transaction Broker Broker Producer Producer Transaction Coordinator Transaction Coordinator Broker (partition 0) Broker (partition 0) Broker (partition 1) Broker (partition 1) initTransactions() PID assigned beginTransaction() send(topic-A, partition-0) send(topic-B, partition-1) sendOffsetsToTransaction() (consumer offsets) commitTransaction() write COMMIT marker write COMMIT marker commit complete All writes become visible atomically when committed
Properties props = new Properties () ;
props . put ( " bootstrap.servers " , " kafka1:9092,kafka2:9092 " ) ;
props . put ( " transactional.id " , " my-app-instance-1 " ) ; // Required
props . put ( " enable.idempotence " , " true " ) ; // Implied
Producer < String , String > producer = new KafkaProducer <>(props);
producer . initTransactions () ;
producer . beginTransaction () ;
producer . send ( new ProducerRecord <>( " topic-a " , " key " , " value1 " ) ) ;
producer . send ( new ProducerRecord <>( " topic-b " , " key " , " value2 " ) ) ;
// Commit consumer offsets as part of transaction
producer . sendOffsetsToTransaction ( offsets, consumerGroupMetadata ) ;
producer . commitTransaction () ;
} catch ( ProducerFencedException | OutOfOrderSequenceException e ) {
} catch ( KafkaException e ) {
producer . abortTransaction () ;
Guarantee Scope Atomic writes All records in transaction succeed or fail together Cross-partition Atomicity across multiple partitions and topics Exactly-once with consumer When combined with read_committed consumers
→ Transactions Guide
Error Type Examples Producer Behavior Retriable NetworkException, NotEnoughReplicasException Automatic retry up to retries Non-retriable InvalidTopicException, RecordTooLargeException Fail immediately Fatal ProducerFencedException, OutOfOrderSequenceException Must close producer
producer . send ( record, (metadata, exception) -> {
log . info ( " Sent to {} offset {} " , metadata . partition () , metadata . offset ()) ;
} else if (exception instanceof RetriableException) {
// Retriable error - already retried and failed
log . error ( " Retriable error after retries: {} " , exception . getMessage ()) ;
// May want to retry at application level or dead-letter
log . error ( " Non-retriable error: {} " , exception . getMessage ()) ;
// Handle bad data, configuration issues, etc.
Configuration Description delivery.timeout.msUpper bound on total time to report success/failure request.timeout.msTime to wait for response to a request retry.backoff.msTime to wait before retrying retry.backoff.max.msMaximum backoff time
→ Error Handling Guide
Practice Rationale Use appropriate batch.size and linger.ms Balance throughput and latency Enable compression for large messages Reduce network and storage Reuse producer instances Connection and buffer overhead Use async sends with callbacks Avoid blocking on every send
Practice Rationale Use acks=all for important data Survive broker failures Enable idempotence Prevent duplicates on retry Set appropriate delivery.timeout.ms Bound total delivery time Handle callbacks/futures properly Detect and handle failures
Practice Rationale Monitor producer metrics Detect issues early Use meaningful client.id Easier debugging and monitoring Close producers properly Flush buffers and release resources Size buffer.memory appropriately Handle traffic bursts