保证向Kafka集群传递多条消息

时间:2016-03-29 09:43:47

标签: java apache-kafka kafka-producer-api

如果我连续几次向Kafka集群发布消息(使用new Producer API),我会从生产者那里获得每条消息的Future

现在,假设我已经将我的生产者配置为max.in.flight.requests.per.connection = 1retries > 0,我可以等到最后的未来,并确保之前的所有生产商都已交付(并按顺序)?或者我需要等待所有期货? 在代码中,我可以执行此操作

Producer<String, String> producer = new KafkaProducer<>(myConfig);
Future<?> f = null;
for(MessageType message : messages){
  f = producer.send(new ProducerRecord<String,String>("myTopic", message.getKey(), message.getValue());
}
try {
  f.get();
} catch(ExecutionException e) {
  //handle exception
}

而不是:

Producer<String, String> producer = new KafkaProducer<>(myConfig);
List<Future<?>> futureList = new ArrayList<>();
for(MessageType message : messages){
  futureList.add(producer.send(new ProducerRecord<String,String>("myTopic", message.getKey(), message.getValue()));
}
try {
  for(Future<?> f : futureList) {
    f.get();
  }
} catch(ExecutionException e) {
  //handle exception
}

并确保如果此处没有捕获任何内容(来自第一个代码段):

try {
  f.get();
} catch(ExecutionException e) {
然后然后我的所有消息都按顺序存储在集群中(无论生产者是否在引擎盖下执行了任何重试),如果出现问题,那么即使它出现了异常第一次遇到这个问题不是最后的未来(我在等待)?

是否有更多奇怪的角落需要注意?

2 个答案:

答案 0 :(得分:1)

您可以执行此操作,但如果您a)将重试设置为无限(或实际上无限),并且b)如果您遇到不可重试的异常,则可以丢弃数据。

为了解释一下,Kafka有两类例外。可逆异常是指如果再次运行它可能会成功的失败。例如,NotEnoughReplicasException表示副本数量少于您的要求,因此请求被拒绝。但是如果一个失败的经纪人重新上线,那么你可能有足够的副本,恢复状态良好,如果再次发送请求,请求就会成功。相反,SerializationException是不可重复的,因为我们没有理由相信如果你再次尝试序列化,结果会有所不同。

生产者重试仅适用于您遇到不可重试的异常的点。因此,如果您从未尝试过任何这些,请使用无限重试,并使用您提到的其他设置,一旦最终的未来得到解决,订购和成功交付将得到保证。但是,由于您可能会遇到不可重试的异常,因此处理每个未来(或回调)肯定要好得多,并确保在请求失败时至少记录一些内容。

答案 1 :(得分:1)

除了Ewen所说的,你还可以在循环中发送完所有消息后拨打flush()。此通话将一直阻止,直到所有期货都已完成,因此在此之后您可以检查期货是否有任何例外情况。你需要坚持所有的期货才能做到这一点。

另一种方法是对您的发送使用回调并存储任何返回的异常,如下所示。再次使用flush确保在检查异常之前已完成所有发送。

Producer<String, String> producer = new KafkaProducer<>(myConfig);
final ArrayList<Exception> exceptionList = new ArrayList<>();

for(MessageType message : messages){
  producer.send(new ProducerRecord<String, String>("myTopic", message.getKey(), message.getValue()), new Callback() {
    @Override
    public void onCompletion(RecordMetadata metadata, Exception exception) {
      if (exception != null) {
        exceptionList.add(exception);
      }
    }
  });
}

producer.flush();

if (!exceptionList.isEmpty()) {
  // do stuff
}