How to reconnect the kafka producer once closed?

advertisements

I have multi thread app which uses producer class to produce messages, earlier i was using below code to create producer for each request.where KafkaProducer was newly built with each request as below:

KafkaProducer<String, byte[]> producer = new KafkaProducer<String, byte[]>(prop);

ProducerRecord<String, byte[]> data = new ProducerRecord<String, byte[]>(topic, objBytes);
producer.send(data, new Callback() {

                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    if (exception != null) {
                        isValidMsg[0] = false;
                        exception.printStackTrace();
                        saveOrUpdateLog(msgBean, producerType, exception);
                        logger.error("ERROR:Unable to produce message.",exception);
                    }
                }
            });
producer.close();

Then I read Kafka docs on producer and come to know we should use single producer instance to have good performance.

Then I created single instance of KafkaProducer inside a singleton class.

Now when & where we should close the producer. Obviously if we close the producer after first send request it wont find the producer to resend messages hence throwing :

java.lang.IllegalStateException: Cannot send after the producer is closed.

OR how we can reconnect to producer once closed. Problem is if program crashes or have exceptions then?


The KafkaProducer.send method is asynchronous, it returns a Future[RecordMetadata]. If you close immediatly after sending, you have a race condition, and due to the buffering nature of the KafkaProducer your message might never get sent.

If your producer is being used throughout the lifetime of your application, don't close it, and let it die once the application terminates. As said in the documentation, the producer is safe to used in a multi-threaded environment and hence you should re-use the same instance.

If you still think you need to close the KafkaProducer in some scenarios, you can add a isClosed flag that inside your Kafka object and monitor it if a consumer needs to resend data again. A rough sketch can be:

object KafkaOwner {
  private var producer: KafkaProducer = ???
  @volatile private var isClosed = false

  def close(): Unit = {
    if (!isClosed) {
      kafkaProducer.close()
      isClosed = true
    }
  }

  def instance: KafkaProducer = {
    this.synchronized {
      if (!isClosed) producer
      else {
        producer = new KafkaProducer()
        isClosed = false
      }
    }
  }
}