I want to create a program that when I send a message and an error occurred, it will retry 3 times then if it still doesn't send it will directly proceed to the dead letter (probably local). Here's what I'm working with right now. I don't know how to implement it properly because I'm super new to spring kafka.
KafkaConfiguration class
public class KafkaConfiguration {
@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, AppConfiguration.bootstrapServers);
props.put(ProducerConfig.RETRIES_CONFIG, "3");
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
props.put(ProducerConfig.ACKS_CONFIG, "all");
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(props);
}
@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
@Bean
public ConsumerFactory<Object, Object> consumerFactory() {
return new DefaultKafkaConsumerFactory<>(consumerProperties());
}
@Bean
public Map<String, Object> consumerProperties() {
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, AppConfiguration.bootstrapServers);
props.put(ConsumerConfig.GROUP_ID_CONFIG, AppConfiguration.groupid);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 15000);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
return props;
}
@Bean
ConcurrentKafkaListenerContainerFactory<?, ?> kafkaListenerContainerFactory(
ConcurrentKafkaListenerContainerFactoryConfigurer configurer,
ObjectProvider<ConsumerFactory<Object, Object>> kafkaConsumerFactory, RetryTemplate retryTemplate) {
ConcurrentKafkaListenerContainerFactory<Object, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
configurer.configure(factory, (ConsumerFactory<Object, Object>) kafkaConsumerFactory);
factory.setRetryTemplate(retryTemplate);
factory.setRecoveryCallback(context -> {
log.error("RetryPolicy limit has been exceeded!");
return null;
});
return factory;
}
@Bean
RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(1000l);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3);
retryTemplate.setRetryPolicy(retryPolicy);
return retryTemplate;
}
}
KafkaApplication class
public class KafkaApplication {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(KafkaApplication.class, args);
TestBean testBean = context.getBean(TestBean.class);
while(true){
for (int i = 0; i < 50; i++) {
try{
Thread.sleep(5000);
} catch (Exception e){
System.out.println("exception" + e.getMessage());
}
testBean.send("This is message " + i);
}
context.getBean(Consumer.Listener.class).latch.await(60, TimeUnit.SECONDS);
}
}
@Bean
public TestBean test() {
return new TestBean();
}
@Bean
public Consumer.Listener listener() {
return new Consumer.Listener();
}
public static class TestBean {
@Autowired
private KafkaTemplate<String, String> template;
public void send(String message) {
this.template.send(AppConfiguration.topic, message);
this.template.flush();
}
}
}
Consumer class
public class Consumer {
public static class Listener {
public final CountDownLatch latch = new CountDownLatch(3);
private final Logger logger = LoggerFactory.getLogger(Consumer.class);
@KafkaListener(topics = AppConfiguration.topic, groupId = AppConfiguration.groupid, containerFactory = "kafkaListenerContainerFactory")
public void listen(@Payload String message, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) {
try{
if (message.startsWith("fail")) {
throw new RuntimeException("failed");
}
System.out.println("Successfully Received: " + message + " (partition: " + partition + ")");
this.latch.countDown();
} catch (Exception e){
System.out.println("Error in sending record");
System.out.println(e);
e.printStackTrace();
}
}
}
}