Spring Kafka
Data Batching
Kafka Producer
Java Programming
Spring Boot

how to send batched data with Spring Kafka producer

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

With Spring Kafka, “send batched data” can mean two different things. You might want Kafka to batch many individual records efficiently on the wire, or you might want your application to send one message whose payload is itself a list of items. Those are related ideas, but they are configured differently.

Kafka Producer Batching

Kafka producers already batch records internally. In normal code, you still call send() once per record, and the Kafka client groups records together before sending them to the broker.

The main producer settings are:

  • 'batch.size'
  • 'linger.ms'
  • 'buffer.memory'

In Spring Boot, you can configure them in application.yml:

yaml
1spring:
2  kafka:
3    producer:
4      batch-size: 16384
5      properties:
6        linger.ms: 20
7        buffer.memory: 33554432

Then send records normally:

java
1import org.springframework.kafka.core.KafkaTemplate;
2import org.springframework.stereotype.Service;
3
4@Service
5public class EventProducer {
6    private final KafkaTemplate<String, String> kafkaTemplate;
7
8    public EventProducer(KafkaTemplate<String, String> kafkaTemplate) {
9        this.kafkaTemplate = kafkaTemplate;
10    }
11
12    public void sendEvent(String event) {
13        kafkaTemplate.send("events", event);
14    }
15}

This is the standard answer when you want better throughput without changing the payload structure.

What linger.ms Really Does

linger.ms tells the producer to wait briefly for more records before sending a batch. A small delay can improve throughput because more records fit into the same request.

That creates a tradeoff:

  • larger linger usually improves batching
  • larger linger can add latency

If your application is throughput-oriented, a small positive linger.ms is often helpful. If it is latency-sensitive, keep it lower.

Sending a List as One Kafka Message

Sometimes the requirement is different: you want to collect many domain objects and publish them as one logical message. In that case, you are not relying on Kafka’s producer batching alone. You are building your own batch payload.

For example, with JSON:

java
1import java.util.List;
2import org.springframework.kafka.core.KafkaTemplate;
3import org.springframework.stereotype.Service;
4
5@Service
6public class BatchProducer {
7    private final KafkaTemplate<String, List<String>> kafkaTemplate;
8
9    public BatchProducer(KafkaTemplate<String, List<String>> kafkaTemplate) {
10        this.kafkaTemplate = kafkaTemplate;
11    }
12
13    public void sendBatch(List<String> records) {
14        kafkaTemplate.send("batched-events", records);
15    }
16}

The consumer then reads a list payload, not many separate Kafka records.

Choosing Between the Two Approaches

Use Kafka’s built-in batching when:

  • each event should stay independent
  • consumers should process one record at a time
  • you want better producer throughput transparently

Use an application-level batch payload when:

  • the consumer expects one grouped unit of work
  • the list must be handled atomically at the application level
  • you want explicit batch semantics in the message contract

These choices affect downstream consumers, retries, and error handling, so they should be made deliberately.

Spring Configuration Example

If you want JSON payloads for list-based messages, configure a serializer that matches the value type.

java
1import java.util.HashMap;
2import java.util.Map;
3import org.apache.kafka.clients.producer.ProducerConfig;
4import org.apache.kafka.common.serialization.StringSerializer;
5import org.springframework.context.annotation.Bean;
6import org.springframework.context.annotation.Configuration;
7import org.springframework.kafka.core.DefaultKafkaProducerFactory;
8import org.springframework.kafka.core.KafkaTemplate;
9import org.springframework.kafka.core.ProducerFactory;
10import org.springframework.kafka.support.serializer.JsonSerializer;
11
12@Configuration
13public class KafkaProducerConfig {
14
15    @Bean
16    public ProducerFactory<String, Object> producerFactory() {
17        Map<String, Object> props = new HashMap<>();
18        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
19        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
20        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
21        return new DefaultKafkaProducerFactory<>(props);
22    }
23
24    @Bean
25    public KafkaTemplate<String, Object> kafkaTemplate() {
26        return new KafkaTemplate<>(producerFactory());
27    }
28}

This setup lets you publish structured batch payloads safely.

Common Pitfalls

One common mistake is assuming KafkaTemplate.send() must receive a list in order for Kafka batching to occur. It does not. Kafka batches ordinary individual sends internally.

Another issue is raising batch.size and linger.ms blindly without measuring throughput and latency. Bigger is not automatically better.

A third problem is using application-level list payloads when consumers actually need item-level retry or partitioning. A single bad item can then poison a whole batch message.

Summary

  • Kafka producer batching already happens when you send records one by one.
  • Tune batch.size and linger.ms when the goal is throughput.
  • Sending a list payload is a different design choice from transport-level batching.
  • Choose application-level batches only when grouped payload semantics are really required.
  • Match serializers and consumer expectations to the batch strategy you choose.

Course illustration
Course illustration

All Rights Reserved.