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
}
}
}
}