Kafka的稳定性

Kafka的消息传输保障机制非常直观。当producer向broker发送消息时,一旦这条消息被commit,由于副本机制(replication)的存在,它就不会丢失。但是如果producer发送数据给broker后,遇到网络问题而造成通信中断,那producer就无法判断该条消息是否已经提交。虽然kafka无法确定网络故障期间发生了什么,但是producer可以retry多次,确保消息已经正确传输到broker中,所以目前kafka实现的是at least once。

幂等性

所谓幂等性,就是对接口的多次调用所产生的结果和调用一次是一致的。生产者在进行重试的时候有可能会重复写入消息,而使用kafka的幂等性功能就可以避免这种情况。

幂等性的条件:

  • 只能保证producer在单个会话内不丢不重,如果producer出现意外挂掉再重启是无法保证的(幂等性情况下,是无法获取之前的状态信息,因此是无法做到跨会话级别的不丢不重)。
  • 幂等性不能跨多个topic-partition,只能保证单个partition内的幂等性,当涉及多个topic-partition时,这中间的状态并没有同步。

producer使用幂等性的示例非常简单,与正常情况下producer使用相比变化不大,只需要把producer的配置enable.idempotence设置为true即可:

Properties props = new Properties();
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG,"true");
props.put("acks","all");
props.put("bootstrap-servers","localhost:9092");
props.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");

KafkaProducer producer = new KafkaProducer(props);
producer.send(new ProducerRecord(topic,"test"));

事务

幂等性并不能跨多个分区运作,而事务可以弥补这个缺憾,事务可以保证对多个分区写入操作的原子性。操作的原子性是指多个操作要么全部成功,要么全部失败,不存在部分成功部分失败的可能。

为了实现事务,应用程序必须提供唯一的transactionalId,这个参数通过客户端程序来进行设定。

props.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG,transactionId);

事务要求生产者开启幂等性特性,因此通过将transactional.id参数设置为非空从而开启事务特性的同时需要将ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG设置为true,如果显示设置为false,则会抛出异常。

KafkaProducer提供了5个与事务相关的方法,详细如下:

//初始化事务,前提是配置了transactionalId
public void initTransactions();
//开启事务
public void beginTransaction();
//为消费者提供事务内的位移提交操作
public void sendOffsetsToTransaction(Map<TopicPartition,OffsetAndMetadata> offsets, String consumerGroupId);
//提交事务
public void commitTransaction();
//终止事务,类似于回滚
public void abortTransaction();

控制器

在Kafka集群中会有一个或者多个broker,其中有一个broker会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态。当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本。当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有broker更新其元数据信息。当使用kafka-topics.sh脚本为某个topic增加分区数量时,同时还是由控制器负责分区的重新分配。

Kafka中的控制器选举的工作依赖于zookeeper,成功竞选为控制器的broker会在zookeeper中创建controller这个临时节点,此临时节点的内容参考如下。

使用zookeeper的图形化客户端工具ZooInspector提供的jar来进行管理。

可靠性保证

可靠性保证:确保系统在各种不同的环境下能够发生一致的行为。

kafka的保证:

  • 保证分区消息的顺序
    • 如果使用同一个生产者往同一个分区写入消息,而且消息B在消息A之后写入。
    • 那么Kafka可以保证消息B的偏移量比消息A的偏移量大,而且消费者会先读取消息A再读取消息B。
  • 只有当消息被写入分区的所有同步副本时(文件系统缓存),它才被认为是已提交
    • 生产者可以选择接收不同类型的确认,控制参数acks
  • 只要还有一个副本是活跃的,那么已提交的消息就不会丢失。
  • 消费者只能读取已经提交的消息。

失效副本
怎么样判定一个分区是否有副本是处于同步失效状态的呢?从Kafka0.9.x版本开始通过唯一的一个参数replica.lag.time.max.ms(默认大小为10000)来控制,当ISR中的一个follower副本滞后leader副本的时间超过参数replica.lag.time.max.ms指定的值时即判定为副本失效,需要将此follower副本剔除出ISR之外。具体实现原理很简单,当follower副本将leader副本的LEO(log end offset,每个分区最后一条消息的位置)之前的日志全部同步时,则认为该follower副本已经追上leader副本,此时更新该副本的lastCaughtUpTimeMs标识。Kafka的副本管理器(ReplicaManager)启动时会启动一个副本过期检测的定时任务,而这个定时任务会定时检查当前时间与副本的lastCaughtUpTimeMs差值是否大于参数replica.lag.time.max.ms指定的值。千万不要错误的认为follower副本只要拉取leader副本的数据就会更新lastCaughtTimeMs,试想当leader副本的消息流入速度大于follower副本的拉取速度时,follower副本一直不断地拉取leader副本的消息也不能与leader副本同步,如果还将此follower副本置于ISR中,那么当leader副本失效,而选取此follower副本为新的leader副本,那么就会严重的消息丢失。

副本复制
Kafka中的每个主题分区都被复制了n次,其中的n是主题的复制因子(replication factor)。这允许kafka在集群服务器发生故障时自动切换到这些副本,以便在出现故障时消息依然可用。kafka的复制是以分区为粒度的,分区的预写日志被复制到n个服务器。在n个副本中,一个副本作为leader,其他副本成为followers。顾名思义,producer只能往leader分区上写数据(读也只能从leader分区上进行),follower只按顺序从leader上复制日志。

一个副本可以不同步leader有如下几个原因:

  • 慢副本:在一定周期时间内follower不能追赶上leader,最常见的原因之一是I/O瓶颈导致follower追加复制消息速度慢于leader拉取速度。
  • 卡住副本:在一定周期时间内follower停止从leader拉取请求。follower replica卡住了是由于GC暂停或follower失效或死亡。
  • 新启动副本:当用户给主题增加副本因子,新的follower不在同步副本列表中,直到他们完全赶上了leader日志。

在这里插入图片描述
在服务端现在只有一个参数需要配置replica.lag.time.max.ms。这个参数解释replicas响应partition leader的最长等待时间。检测卡住或失败副本,如果一个replica失败导致发送拉取请求时间间隔超过replica.lag.time.max.ms,Kafka会认为此replica已经死亡会从同步副本列表中移除。检测慢副本机制发生了变化,如果一个replica开始落后leader超过replica.lag.time.max.ms,kafka会认为太缓慢并且会从同步副本列表中移除,除非replica请求leader时间间隔大于replica.lag.time.max.ms,因此即使leader使流量激增和大批量写消息。kafka也不会从同步副本列表中移除该副本。

一致性保证

  • 在leader宕机后,只能从ISR列表中选取新的leader,无论ISR中哪个副本被选为新的leader,它都知道HW之前的数据,可以保证在切换了leader后,消费者可以继续看到HW之前已经提交的数据。
  • HW的截断机制,选出了新的leader并不能保证已经完全同步了之前leader的所有数据,只能保证HW之前的数据是同步过的,此时所有的follower都要将数据截断到HW的位置,再和新的leader同步数据,来保证数据一致。当宕机的leader恢复,发现新的leader中的数据和自己持有的数据不一致,此时宕机的leader会将自己的数据截断到宕机之前的HW位置,然后同步新leader的数据,宕机的leader活过来也像follower一样同步数据,来保证数据的一致性。

数据丢失场景在这里插入图片描述在这里插入图片描述
数据不一致场景
在这里插入图片描述
在这里插入图片描述
Kafka0.11.0版本解决方案
造成上述两个问题的根本原因在于HW值被用于衡量副本备份的成功与否以及在出现failure时作为日志截断的依据,但HW的更新是异步的,特别是需要额外的FETCH请求处理流程才能更新,故这中间发生的任何崩溃都可能导致HW值得过期。鉴于这些原因,Kafka引入了leader epoch来取代HW值。leader端多开辟一段内存区域专门保存leader的epoch信息,这样即使出现上面的两个场景也能很好地规避这些问题。

所谓leader epoch实际上是一对值,(epoch,offset)。epoch表示leader的版本号,从0开始,当leader变更过1次时epoch就会+1,而offset则对应于该epoch版本的leader写入第一条消息的位移,因此假设有两对值,(0,0),(1,120),则表示第一个leader从位移0开始写入消息,共写了120条[0,119],而第二个leader版本号是1,从位移120处开始写入消息。

leader broker中会保存这样的一个缓存,并定期地写入到一个checkpoint文件中。

避免数据丢失:
在这里插入图片描述
避免数据不一致:
在这里插入图片描述

消息重复场景及解决方案

生产者端重复

生产发送的消息没有收到正确的broker响应,导致producer重试。

producer发出一条消息,broker因为网络等种种原因发送得到一个发送失败的响应或者网络中断,然后producer收到一个可恢复的Exception重试消息导致消息重复。

解决方案:
1、启动kafka幂等性。
2、ack=0,不重试。可能会丢失消息。

消费端重复

数据消费完没有及时提交offset到broker。

解决方案:
1、取消自动提交。每次消费完或者程序退出时手动提交。这可能也没法保证一条重复。
2、下游做幂等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值