我有多线程应用程序,它使用生产者类来生成消息,早些时候我使用下面的代码为每个请求创建生成器。其中KafkaProducer是每个请求新建的,如下所示:
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();
然后我读了关于制作人的Kafka文档并且知道我们应该使用单个生成器实例来获得良好的性能。
然后我在单例类中创建了单个KafkaProducer实例。
现在&amp;我们应该关闭生产者。显然,如果我们在第一次发送请求后关闭生产者,它将不会发现生产者重新发送消息,因此抛出:
java.lang.IllegalStateException: Cannot send after the producer is closed.
或者我们如何在关闭后重新连接到生产者。 问题是如果程序崩溃或有例外吗?
答案 0 :(得分:2)
KafkaProducer.send
方法是异步的,它返回Future[RecordMetadata]
。如果您在发送后立即close
,则会遇到竞争条件,并且由于KafkaProducer
的缓冲性质,您的消息可能永远不会被发送。
如果您的应用程序在应用程序的整个生命周期中都在使用,请不要关闭它,并在应用程序终止后让它死掉。如文档中所述,生产者可以安全地在多线程环境中使用,因此您应该重复使用相同的实例。
如果您仍然认为在某些情况下需要关闭KafkaProducer
,则可以在Kafka对象中添加isClosed
标记,并在消费者需要重新发送数据时对其进行监控。草图可以是:
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
}
}
}
}
答案 1 :(得分:1)
如KafkaProducer
的javadoc所述:
public void close()
Close this producer. This method blocks until all previously sent requests complete.
This method is equivalent to close(Long.MAX_VALUE, TimeUnit.MILLISECONDS).
src:https://kafka.apache.org/0110/javadoc/org/apache/kafka/clients/producer/KafkaProducer.html#close()
因此,即使您在发送后立即致电关闭,也无需担心您的邮件将无法发送。
如果您打算多次使用KafkaProducer,请在完成使用后关闭它。如果您仍希望保证在方法完成之前实际发送消息而不在缓冲区中等待,则使用KafkaProducer#flush()
将阻塞,直到发送当前缓冲区为止。如果您愿意,也可以阻止Future#get()
。
如果您不打算关闭KafkaProducer,还有需要注意的 (例如,在短期应用中,您只需发送一些数据和应用发送后立即终止)。 KafkaProducer IO线程是一个守护程序线程,这意味着JVM不会等到该线程完成终止VM。因此,为了确保您的邮件实际发送,请使用KafkaProducer#flush()
,no-arg KafkaProducer#close()
或阻止Future#get()
。
答案 2 :(得分:0)
Kafka生产者应该是线程安全的,节省它的线程池。你可能想用
producer.flush();
而不是
producer.close();
让制片人保持开放状态,直到节目终止,或直到你确定不再需要它为止。
如果您仍想关闭制作人,请按需重新创建。
producer = new KafkaProducer<String, byte[]>(prop);