In a Spring Boot application, Apache Kafka listeners start automatically and consume messages from Kafka topics. Sometimes we may need to start or stop listeners dynamically based on application needs, which can be done using KafkaListenerEndpointRegistry.
- Start or stop Kafka listeners dynamically.
- Improve resource usage by controlling message consumption.
- Manage listener lifecycle programmatically.
Kafka Listener
A Kafka Listener is a Spring bean that listens to Kafka topics and processes incoming messages asynchronously. It is configured using the @KafkaListener annotation and starts automatically when the application runs.
- Listens to messages from Apache Kafka topics.
- Processes messages asynchronously in a Spring Boot application.
- Configured using the @KafkaListener annotation.
Different Approaches to Start/Stop a Kafka Listener
1. Controlling Listeners Based on Message Processing
This approach starts Kafka listeners only when messages need to be processed and stops them after processing to save resources. It is useful for handling burst traffic and avoiding unnecessary listener activity when no messages are available.
Example: Start the listener when a message is detected and stop it after the message processing is completed.
@Component
public class KafkaListenerControl {
@Autowired
// Injecting Kafka listener registry
private KafkaListenerEndpointRegistry registry;
// Start the listener if it is not already running
public void startListener(String listenerId) {
// Get the listener container by ID
MessageListenerContainer container =
registry.getListenerContainer(listenerId);
// Check if container exists and is not running
if (container != null && !container.isRunning()) {
// Start the listener container
container.start();
// Log listener start
logger.info("Listener {} started.", listenerId);
}
}
// Stop the listener if it is currently running
public void stopListener(String listenerId) {
// Get the listener container by ID
MessageListenerContainer container =
registry.getListenerContainer(listenerId);
// Check if container exists and is running
if (container != null && container.isRunning()) {
// Stop the listener container
container.stop();
// Log listener stop
logger.info("Listener {} stopped.", listenerId);
}
}
}
This approach allows a good control over Kafka listeners and it enable us to start and stop them based on specific conditions or messages being processed.
2. Using @KafkaListener with autoStartup Property
The autoStartup property controls whether a Kafka listener starts automatically. Setting autoStartup=false prevents it from starting with the application.
Example : The listener will not start automatically when the application runs because autoStartup is set to false
@KafkaListener(id="dynamicListener", topics="test-topic", autoStartup="false")
public void processMessage(String message){
System.out.println(message);
}
3. Manual Control with KafkaListenerEndpointRegistry
KafkaListenerEndpointRegistry allows us to start or stop Kafka listeners programmatically. This is useful when listeners need to be controlled based on events such as API calls or specific application conditions.
Example:
@Autowired
private KafkaListenerEndpointRegistry registry;
// Start Listener
public void startListener(String id){
registry.getListenerContainer(id).start();
}
// Stop Listener
public void stopListener(String id){
registry.getListenerContainer(id).stop();
}
Implementing Start/Stop Kafka Listener Dynamically
Step 1: Create a Message Class
Integration of Apache Kafka with Spring Boot, refer this article: Spring Boot - Integration with Kafka .Create a simple model class to represent the message received from Kafka.
- Define required fields for the message.
- This class will be used for message serialization and deserialization.
Define a Message Class
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
private String message;
}
Step 2: Configure Kafka Consumer
Create a Kafka configuration class to configure the consumer settings.
- Configure bootstrap server and consumer group ID.
- Define
ConsumerFactoryandKafkaListenerContainerFactorybeans.
Configure Kafka Consumer:
@Configuration
public class KafkaConsumerConfig {
private String kafkaUrl = "localhost:9092";
@Bean
public ConsumerFactory<String, Message>
messageConsumerFactory() {
JsonDeserializer<Message> deserializer =
new JsonDeserializer<>(Message.class, false);
deserializer.setRemoveTypeHeaders(false);
deserializer.addTrustedPackages("*");
Map<String, Object> config = new HashMap<>();
config.put
(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaUrl);
config.put
(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
config.put
(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
config.put
(ConsumerConfig.GROUP_ID_CONFIG, "group-1");
return new DefaultKafkaConsumerFactory<>
(config, new StringDeserializer(), deserializer);
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, Message>
messageListenerFactory() {
ConcurrentKafkaListenerContainerFactory<String, Message>
containerFactory = new ConcurrentKafkaListenerContainerFactory<>();
containerFactory.setConsumerFactory(messageConsumerFactory());
return containerFactory;
}
}
Step 3: Create Kafka Listener
Create a listener method using the @KafkaListener annotation.
Example parameters used in the annotation:
- id: Unique identifier for the listener container.
- topics: Kafka topic name to consume messages from.
- groupId: Consumer group ID.
- containerFactory: Factory used to create listener containers.
- autoStartup: Determines whether the listener starts automatically.
Example:
@KafkaListener(
id = "dynamicListener",
topics = "test-topic",
groupId = "test-group",
autoStartup = "false"
)
public void processMessage(Message message) {
logger.info("Processing message: {}", message);
}
Important Points
- Setting
autoStartup=falseprevents the listener from starting automatically. - The listener will start only when triggered manually.
4. Control Kafka Listener (Start/Stop):
The KafkaListenerEndpointRegistry class can be used to get a Kafka Listener Container by listenerId. Here we have used @KafkaListener annotation to stating the bean method as a listener for the Kafka Listener Container. The Kafka Listener can now be started or stopped using this container.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.config.KafkaListenerEndpointRegistry;
import org.springframework.kafka.listener.MessageListenerContainer;
import org.springframework.stereotype.Component;
@Component
public class KafkaListenerAutomation {
private final Logger logger =
LoggerFactory.getLogger(KafkaListenerAutomation.class);
@Autowired
KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry;
public boolean startListener(String listenerId) {
MessageListenerContainer listenerContainer =
kafkaListenerEndpointRegistry.getListenerContainer(listenerId);
assert listenerContainer != null : "Listener not found!";
listenerContainer.start();
logger.info("{} Kafka Listener Started", listenerId);
return true;
}
public boolean stopListener(String listenerId) {
MessageListenerContainer listenerContainer
= kafkaListenerEndpointRegistry.getListenerContainer(listenerId);
assert listenerContainer != null : "Listener not found!";
listenerContainer.stop();
logger.info("{} Kafka Listener Stopped.", listenerId);
return true;
}
}
API Endpoints to Control Kafka Listener:
Using API endpoints, we can start or stop a specific Kafka listener by providing the listenerId.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StartOrStopListenerController {
@Autowired
KafkaListenerAutomation kafkaListenerAutomation;
@GetMapping("/start")
public void start(@RequestParam("id") String listenerId) {
kafkaListenerAutomation.startListener(listenerId);
}
@GetMapping("/stop")
public void stop(@RequestParam("id") String listenerId) {
kafkaListenerAutomation.stopListener(listenerId);
}
}
Output:
1. Kafka Listener started:
Access the following endpoint to start a listener:
GET /start?id=id-1
When the listener is started, you will see the below log message:

2. Kafka Listener received message:
When the listener processes a message, you should see a log message similar like below:
Message received: -> {message: "Your Message Content"}
3. Kafka Listener stopped:
Access the below endpoint to stop the listener:
GET /stop?id=id-1
When the listener is stopped, you will see the below log message:
