kafka学习系列:消息发送确认机制,避免程序退出导致数据丢失的情况

文章目录

  • 场景
  • 环境
  • 正文
    • spring-kafka发送消息的介绍
    • 消息发送确认机制
      • 一、同步阻塞
      • 二、异步等待结果返回
  • 总结
  • 参考链接
  • 随缘求赞

场景

在使用spring-kafka进行功能开发的时候,思考过这样一个问题:假如使用信号量的方式(不了解的,可以点击我的这篇文章《Java学习系列:使用SignalHandler来处理Linux信号量,控制程序结束的步骤》 进行了解)来终止程序,虽然我们使用了kafkaTemplate.send方法发送了,但是假如程序在发送过程就关闭了,是否就会造成数据丢失?即我们调用了kafkaTemplate.send方法发送了数据,认为数据已经发送了;但是程序关闭的时候,导致数据未发送成功,进而导致了数据丢失情况的发生。

环境

软件 版本
JDK 8
Kafka 2.0.1
spring-boot 2.1.8.RELEASE

正文

spring-kafka发送消息的介绍

spring-kafka提供了几种消息处理的机制,具体的方法如下:

ListenableFuture<SendResult<K, V>> send(String topic, V data);

ListenableFuture<SendResult<K, V>> send(String topic, K key, V data);

ListenableFuture<SendResult<K, V>> send(String topic, Integer partition, K key, V data);

ListenableFuture<SendResult<K, V>> send(String topic, Integer partition, Long timestamp, K key, V data);

ListenableFuture<SendResult<K, V>> send(ProducerRecord<K, V> record);

ListenableFuture<SendResult<K, V>> send(Message<?> message);

这些是从类org.springframework.kafka.core.KafkaOperations里面截取出来的,而KafkaTemplate是继承了KafkaOperations,并实现了里面的方法。如果细心的人可以发现,其实每个方法基本都会返回一个ListenableFuture对象。通过查看KafkaTemplate的源码,我们可以看到里面的多个send方法都会指向protected ListenableFuture> doSend(final ProducerRecord producerRecord),下面贴一下这个方法的内部实现:

protected ListenableFuture<SendResult<K, V>> doSend(final ProducerRecord<K, V> producerRecord) {
	if (this.transactional) {
		Assert.state(inTransaction(),
				"No transaction is in process; "
					+ "possible solutions: run the template operation within the scope of a "
					+ "template.executeInTransaction() operation, start a transaction with @Transactional "
					+ "before invoking the template method, "
					+ "run in a transaction started by a listener container when consuming a record");
	}
	final Producer<K, V> producer = getTheProducer();
	if (this.logger.isTraceEnabled()) {
		this.logger.trace("Sending: " + producerRecord);
	}
	final SettableListenableFuture<SendResult<K, V>> future = new SettableListenableFuture<>();
	producer.send(producerRecord, buildCallback(producerRecord, producer, future));
	if (this.autoFlush) {
		flush();
	}
	if (this.logger.isTraceEnabled()) {
		this.logger.trace("Sent: " + producerRecord);
	}
	return future;
}

从上面的源码,我们可以知道,处理之后,是返回了SettableListenableFuture对象。那么,这个对象是做什么用的呢?我们可以看一下里面的类图:
kafka学习系列:消息发送确认机制,避免程序退出导致数据丢失的情况_第1张图片
从上面的类图,我们可以发现,其实都是继承了Futrue接口。说到这里,就得说一下Future是什么东西了。Future接口是在Java 5被引入,设计初衷是提供一种异步计算,返回一个执行运算结果的引用,当运算结束之后,这个引用将会被返回给调用方。这样就可以将耗时时间比较久的运算先隔离开,自己去做其他运算,等处理好,再来接收结果。所以,我们在使用kafkaTemplate.send方法的时候,其实是会将数据放到一个队列里面,等待数据累积到一定阶段或者一定的时间,就会将数据发送出去。这样做是为了减少数据传输的次数,加快发送效率。具体的代码可以查看org.apache.kafka.clients.producer.internals.ProducerBatch。而这个过程耗费的实际是不确定的,所以没有特殊要求的话,我们一般是不做同步阻塞等待消息结果返回,而是使用回调函数的方式,让spring-kafka发送消息之后,主动调用回调函数,告知我们数据已经发送成功了。那么,该怎么做?这就是接下来的重点了。

消息发送确认机制

我们一般使用两种方式来对消息发送的结果进行处理,一种是同步阻塞等待结果返回,一种是异步等待结果返回。接下来为大家说明两种情况。

一、同步阻塞

发送完,立即调用Future等待返回,就是同步阻塞。请看代码:

try {
    kafkaTemplate.send(topic, partition,
                        String.valueOf(Math.round(Math.random() * 100)),
                        message).get(100, TimeUnit.SECONDS);
    handleSuccess(data);
}
catch (ExecutionException e) {
    handleFailure(data, record, e.getCause());
}
catch (TimeoutException | InterruptedException e) {
    handleFailure(data, record, e);
}

二、异步等待结果返回

发送完,获取对应的ListenableFuture对象,并添加对应的回调函数,等待消息发送完毕之后,调用该回调函数方法。具体代码如下:

ListenableFuture<SendResult<String,String>> future =  kafkaTemplate.send(topic, partition,
                        String.valueOf(Math.round(Math.random() * 100)),
                        message);
future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
    @Override
    public void onSuccess(SendResult<String, String> result) {
        log.trace("发送消息成功,发送主题为:",topic);
    }

    @Override
    public void onFailure(Throwable ex) {
        log.error("发送消息失败,消息主题为 {},异常消息为 :{}",topic,ex);
        handleFailureData(topic, message);
    }
});

而实际项目使用中,我是使用第二种方式,这样既不会耽误消息的发送,也不会错过对应的消息结果,比单一的同步阻塞方法要好多了。

总结

通过研究spring-kafka的文档和对应的源码,实现了消息发送确认的机制:添加对应的回调函数,等待消息发送完毕之后,调用回调函数进行记录。从源码当中,也学到了不少的东西和比较好的设计理念。

参考链接

spring-kafka-docs

随缘求赞

如果我的文章对大家产生了帮忙,可以在文章底部点个赞或者收藏;
如果有好的讨论,可以留言;
如果想继续查看我以后的文章,可以左上角点击关注
kafka学习系列:消息发送确认机制,避免程序退出导致数据丢失的情况_第2张图片

你可能感兴趣的:(kafka学习系列,Java学习系列,大数据,java,kafka,消息确认机制,数据丢失)