本文转载自微信公众号“微科技”,作者微科技。转载本文请联系微科公众号。大家好,我是Tom兄~Kafka消息框架,大家一定不陌生,很多人在工作中都接触过。其核心思想是通过一个高性能的MQ服务将生产和消费两个系统连接起来,实现系统间的解耦,具有很强的可扩展性。你可能会有疑问,如果中间一个环节坏了怎么办?这种情况我们称之为消息丢失,会造成系统间数据不一致。那么如何解决这个问题呢?需要从生产端、MQ服务器端、消费端三个维度来处理。1.生产端生产端的职责是确保生产的消息能够到达MQ服务器。这里我们需要一个response来判断操作是否成功。Futuresend(ProducerRecordrecord,Callbackcallback)比如上面的代码使用了一个Callback函数来判断消息是否发送成功。如果失败了,我们需要赔偿。另外,为了提高发送的灵活性,Kafka提供了多种参数供不同业务选择1.1参数acks该参数表示有多少分区副本收到消息才认为发送成功。acks=0,只要消息发送成功,producer不等待server节点的响应。acks=1,表示producer收到leader分区的response。收到消息后,生产端就认为成功了。这种配置是最安全的,但由于同步的节点较多,吞吐量会降低。1.2参数retries表示生产端的重试次数。如果重试次数用完,消息仍然失败,消息会暂存在本地磁盘,待服务恢复后重新发送。建议取值retries=31.3参数retry.backoff.m消息发送超时或失败后的间隔重试时间。通常推荐的设置时间是300毫秒。这里要特别注意一个特殊的情况。如果MQ服务没有正常响应,并不一定代表消息发送失败。也有可能是响应刚好赶上网络抖动,响应超时。当生产端完成这些后,就可以保证消息发送成功,但是可能会发送多次,导致消息重复。稍后我们将讨论解决方案。2、MQ服务器作为消息的存储介质,MQ服务器可能会丢失消息。比如某个分区突然挂了,如何保证这个分区的数据不会丢失?我们将引入副本的概念,通过备份来解决这个问题。具体可以设置哪些参数?2.1参数replication.factor表示分区副本数。如果replication.factor>1,当leader副本失效时,follower副本会被选举为leader继续提供服务。2.2参数min.insync.replicas表示ISR的最小副本数,通常设置min.insync.replicas>1,这样才有可用的follower副本进行替换,保证消息不丢失2.3参数是否unclean.leader.election.enable可以设置non-ISR集合中的副本被选举为领导者副本。如果设置为true,follower副本的同步消息进度远远落后,此时会被选举为leader,会导致消息丢失,慎用。3.消费端消费端要做的就是处理消息的完整消费。但是有一个提交位移的步骤。有的同学考虑到业务处理时间比较长,会开一个单独的线程拉取消息存储到本地内存队列中,然后再建立一个线程池并行处理业务逻辑。这种设计存在风险。如果本地消息没有处理,服务器宕机,消息就会丢失。正确做法:拉取消息---业务处理---提交消费位移关于提交位移,kafka提供了一个集中的参数配置参数enable.auto.commit来表示是否自动提交消费位移。如果消息被拉取,业务逻辑还没有处理完,消费者位移提交但是消费者挂了,消费者恢复或者另一个消费者接管了分片,不能再拉取消息,会导致消息迷路了。因此,我们通常设置enable.auto.commit=false来手动提交消费位移。Listmessages=consumer.poll();processMsg(消息);consumer.commitOffset();此解决方案将导致另一个问题。看这张图:拉取消息4~消息8,业务处理最后提交消费位移的时候,不幸的是系统宕机了,最终提交的位移没有保存到MQ服务器。下次拉取消息时,还是从消息4拉取,但是这部分消息已经处理过了,这将导致重复消费。如何解决重复消费,避免数据不一致首先要解决MQ服务端的重复消息。kafka0.11.0版本后,每条消息都有唯一的消息id,MQ服务采用空间换时间的方式自动过滤处理重复的消息,保证接口的幂等性。但是这并不能从根本上解决消息重复的问题。即使MQ服务中存储的消息不重复,消费者使用pull的方式。如果重复拉取消息,也会导致重复消费。如何解决这个场景问题?方案一:只拉取一次(消费者拉取消息后,先提交offset再处理消息),但是如果系统宕机,业务处理没有正常结束,后面就不会拉取这些消息了,会导致数据不一致。几乎没有使用过。方案二:允许拉取重复消息,但消费端自己做幂等控制。保证只消费一次成功。有很多幂等技术方案。我们可以使用数据表或者Redis缓存来存储和处理标识符。每次拉取一条消息,处理前先检查处理状态,再决定是处理还是丢弃这条消息。