Spring Kafka
Kafka Consumer
Message Consumption
Batch Processing
Performance Optimization

How to increase the number of messages consumed by Spring Kafka Consumer in each batch?

Master System Design with Codemia

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

Introduction

If a Spring Kafka consumer is processing too few records per poll, the fix is usually a combination of Kafka consumer settings and listener configuration. Increasing batch size is not just about one property like max.poll.records; you also need to enable batch listeners, make sure enough data is available to fetch, and confirm the application can process larger polls without timing out.

Enable a Batch Listener in Spring Kafka

Spring Kafka can deliver one record at a time or a whole batch. If your listener method takes a single string, increasing fetch settings alone will not give you a batch-oriented handler.

java
1import java.util.List;
2import org.springframework.kafka.annotation.KafkaListener;
3import org.springframework.stereotype.Component;
4
5@Component
6public class OrderBatchConsumer {
7
8    @KafkaListener(topics = "orders", containerFactory = "batchFactory")
9    public void consume(List<String> messages) {
10        System.out.println("Batch size = " + messages.size());
11        for (String message : messages) {
12            System.out.println(message);
13        }
14    }
15}

That method signature tells Spring to hand over a batch instead of invoking the listener once per record.

Configure Consumer Fetch and Poll Limits

The most relevant Kafka settings are max.poll.records, fetch.min.bytes, fetch.max.wait.ms, and max.partition.fetch.bytes.

yaml
1spring:
2  kafka:
3    consumer:
4      bootstrap-servers: localhost:9092
5      group-id: orders-group
6      auto-offset-reset: earliest
7      max-poll-records: 500
8      fetch-min-bytes: 65536
9      fetch-max-wait-ms: 500
10      properties:
11        max.partition.fetch.bytes: 1048576

max.poll.records limits how many records the client will return per poll. fetch.min.bytes encourages the broker to wait for more data before replying. fetch.max.wait.ms places an upper bound on that wait.

If the topic has low traffic, raising max.poll.records alone will not magically create larger batches. There must actually be enough messages available.

Wire a Batch-Capable Listener Container

You also need a listener container factory with batch mode enabled.

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
4import org.springframework.kafka.core.ConsumerFactory;
5
6@Configuration
7public class KafkaBatchConfig {
8
9    @Bean
10    public ConcurrentKafkaListenerContainerFactory<String, String> batchFactory(
11            ConsumerFactory<String, String> consumerFactory) {
12        ConcurrentKafkaListenerContainerFactory<String, String> factory =
13                new ConcurrentKafkaListenerContainerFactory<>();
14        factory.setConsumerFactory(consumerFactory);
15        factory.setBatchListener(true);
16        factory.setConcurrency(3);
17        return factory;
18    }
19}

The concurrency value should reflect topic partitions and the actual workload. More threads help only if partitions and CPU capacity justify them.

Balance Throughput Against Processing Time

Larger batches increase throughput, but they also increase the time spent inside one poll cycle. If processing takes too long, Kafka may consider the consumer unhealthy and trigger a rebalance.

That means you need to watch max.poll.interval.ms, processing latency, and memory use. A consumer that pulls 500 records but takes several minutes to process them can become less stable than a smaller, faster batch consumer.

Measure Before and After Tuning

Use logs and metrics to confirm the changes are real. At minimum, record how many messages each batch contains and how long each batch takes to process.

java
1long start = System.currentTimeMillis();
2System.out.println("Received batch of size " + messages.size());
3// process messages
4long elapsed = System.currentTimeMillis() - start;
5System.out.println("Processing time ms = " + elapsed);

Without measurement, it is easy to mistake higher fetch limits for better throughput when the real bottleneck is downstream storage or business logic.

Common Pitfalls

  • Increasing max.poll.records while still using a single-record listener method.
  • Expecting larger batches on a low-volume topic where enough messages are not available.
  • Raising batch size without checking processing time against Kafka rebalance settings.
  • Adding high concurrency even when the topic has too few partitions to use it.
  • Tuning fetch properties without measuring batch size and end-to-end throughput afterward.

Summary

  • Enable batch listeners if you want Spring Kafka to deliver a list of records.
  • Tune max.poll.records together with fetch-related settings.
  • Make sure the broker actually has enough data to form larger batches.
  • Watch processing time so bigger polls do not destabilize the consumer.
  • Measure throughput and latency before deciding the tuning is successful.

Course illustration
Course illustration

All Rights Reserved.