如何使用Kafka可靠地发送消息-《Kafka权威指南(第二版)》阅读笔记

可靠性是系统而不是某个独立组件的一个属性,所以,在讨论Kafka的可靠性保证时,需要从系统的整体出发。说到可靠性,那些与Kafka集成的系统与Kafka本身一样重要。正因为可靠性是系统层面的概念,所以它不只是某个个体的事情。Kafka管理员、Linux系统管理员、网络和存储管理员,以及应用程序开发者,所有人必须协同作战才能构建出一个可靠的系统。

broker如何影响消息存储的可靠性

broker中有3个配置参数会影响Kafka的消息存储可靠性。

复制系数

Kafka的复制机制和分区多副本架构是Kafka可靠性保证的核心。把消息写入多个副本可以保证Kafka在发生崩溃时仍然能够提供消息的持久性。主题级别的配置参数是 replication.factor。在broker级别,可以通过default.replication.factor 来设置自动创建的主题的复制系数。Kafka默认的复制系数是3。

确定一个主题需要几个副本时要考虑可用性、持久性、吞吐量、端到端延迟、成本等因素。

不彻底的首领选举

当分区的首领不可用时,一个同步副本将被选举为新首领。如果在选举过程中未丢失数据,也就是说所有同步副本都包含了已提交的数据,那么这个选举就是“彻底”的。

如果一个分区的首领副本变得不可用,而其他副本没有资格成为首领(通常是因为缺少数据),那么这个分区将没有首领,也就不可用了。解决这个问题的一种方法是触发不彻底的首领选举,也就是选举一个本来没有资格成为首领的副本作为首领。这可能导致数据丢失——所有写入旧首领但未被复制到新首领的消息都将丢失。

总的来说,如果允许不同步副本成为首领,那么就要承担丢失数据和消费者读取到不一致的数据的风险。如果不允许它们成为首领,那么就要接受较低的可用性,因为必须等待原先的首领恢复到可用状态。在默认情况下,unclean.leader.election.enable 的值是 false,也就是不允许不同步副本成为首领。这是最安全的选项,因为它可以保证数据不丢失。这也意味着在之前描述的极端不可用场景中,一些分区将一直不可用,直到手动恢复。

最少同步副本

min.insync.replicas 参数可以配置在主题级别和broker级别。

尽管为一个主题配置了3个副本,还是会出现只剩下一个同步副本的情况。如果这个同步副本变为不可用,则必须在可用性和一致性之间做出选择,而这是一个两难的选择。根据Kafka对可靠性保证的定义,一条消息只有在被写入所有同步副本之后才被认为是已提交的,但如果这里的“所有”只包含一个同步副本,那么当这个副本变为不可用时,数据就有可能丢失。如果想确保已提交的数据被写入不止一个副本,就要把最少同步副本设置得大一些。对于一个包含3个副本的主题,如果 min.insync.replicas 被设置为2,那么至少需要有两个同步副本才能向分区写入数据。

保持副本同步

不同步副本会降低总体可靠性,所以要尽量避免出现这种情况。一个副本可能在两种情况下变得不同步:要么它与ZooKeeper断开连接,要么它从首领复制消息滞后。对于这两种情况,Kafka提供了两个broker端的配置参数。

zookeeper.session.timeout.ms 是允许broker不向ZooKeeper发送心跳的时间间隔。如果超过这个时间不发送心跳,则ZooKeeper会认为broker已经“死亡”,并将其从集群中移除。在Kafka 2.5.0中,这个参数的默认值从6秒增加到了18秒,以提高Kafka集群在云端的稳定性,因为云环境的网络延迟更加多变。

如果一个副本未能在 replica.lag.time.max.ms 指定的时间内从首领复制数据或赶上首领,那么它将变成不同步副本。在Kafka 2.5.0中,这个参数的默认值从10秒增加到了30秒,以提高集群的弹性,并避免不必要的抖动。需要注意的是,这个值也会影响消费者的最大延迟——值越大,等待一条消息被写入所有副本并可被消费者读取的时间就越长,最长可达30秒。

持久化到磁盘

消息并不是实时持久化到磁盘上的,Kafka会在重启之前和关闭日志片段(默认1 GB大小时关闭)时将消息冲刷到磁盘上,或者等到Linux系统页面缓存被填满时冲刷。因此即使消息还没有被持久化到磁盘上,Kafka也可以向生产者发出确认,这取决于已接收到消息的副本的数量。我们可以让broker更频繁地将消息持久化到磁盘上。配置参数 flush.messages 用于控制未同步到磁盘的最大消息数量,flash.ms 用于控制同步频率。

在可靠的系统中使用生产者

根据可靠性需求配置恰当的 acks

acks指定了生产者在多少个分区副本收到消息的情况下才会认为消息写入成功,可设置的值有3个:

acks=0:生产者不会等待任何来自broker的响应。

acks=1:只要集群的首领副本收到消息,生产者就会收到消息成功写入的响应。

acks=all:只有当所有副本全部收到消息时,生产者才会收到消息成功写入的响应。

在代码里正确处理异常

生产者需要处理的错误包括两个部分:一部分是由生产者自动处理的错误,另一部分是需要开发者手动处理的错误。生产者可以自动处理可重试的错误。当生产者向broker发送消息时,broker可以返回一个成功响应或者错误响应。错误响应可以分为两种,一种是在重试之后可以解决的,另一种是无法通过重试解决的。

如果你的目标是不丢失消息,那么就让生产者在遇到可重试错误时保持重试。重试发送消息存在一定的风险,因为如果两条消息都成功写入,则会导致消息重复。如果把 enable.idempotence 参数设置为 true,那么生产者就会在消息里加入一些额外的信息,broker可以使用这些信息来跳过因重试导致的重复消息。

使用生产者内置的重试机制可以在不造成消息丢失的情况下轻松地处理大部分错误,但开发人员仍然需要处理以下这些其他类型的错误。

  • 不可重试的broker错误,比如消息大小错误、身份验证错误等。

  • 在将消息发送给broker之前发生的错误,比如序列化错误。

  • 在生产者达到重试次数上限或重试消息占用的内存达到上限时发生的错误。

  • 超时。

在可靠的系统中使用消费者

消费者在读取消息时不丢失消息的关键是跟踪哪些消息是已经读取过的,哪些消息是还未读取的。为了保证消费者行为的可靠性,需要注意以下4个非常重要的配置参数。

group.id

如果两个消费者具有相同的群组ID,并订阅了同一个主题,那么每个消费者只能读取到所有消息的一个子集。

auto.offset.reset

这个参数指定了当没有偏移量(比如在消费者首次启动时)或请求的偏移量在broker上不存在时(第4章已经介绍过这种场景)消费者该作何处理。这个参数有两个值,一个是 earliest,如果配置了这个值,那么消费者将从分区的开始位置读取数据,即使它没有有效的偏移量。这会导致消费者读取大量的重复数据,但可以保证最少的数据丢失。另一个值是 latest,如果配置了这个值,那么消费者将从分区的末尾位置读取数据。这样可以减少重复处理消息,但很有可能会错过一些消息。

enable.auto.commit

你可以决定让消费者自动提交偏移量,也可以在代码里手动提交偏移量。自动提交可以少操心一些事情。如果是在消费者的消息轮询里处理数据,那么自动提交可以确保不会意外提交未处理的偏移量。自动提交的主要缺点是我们无法控制应用程序可能重复处理的消息的数量,比如消费者在还没有触发自动提交之前处理了一些消息,然后被关闭。如果应用程序的处理逻辑比较复杂(比如把消息交给另外一个后台线程去处理),那么就只能使用手动提交了,因为自动提交机制有可能会在还没有处理完消息时就提交偏移量。

auto.commit.interval.ms与第三个参数有直接的联系。如果选择使用自动提交,那么可以通过这个参数来控制提交的频率,默认每5秒提交一次。一般来说,频繁提交会增加额外的开销,但也会降低重复处理消息的概率。

手动提交偏移量

总是在处理完消息后提交偏移量,同时考虑正确性和性能方面的问题。

验证系统可靠性

最后,我们还需要对系统可靠性做3个层面的验证。

  • 验证配置是否符合预期

  • 验证应用程序是否正确妥善地处理了消息,包括发生异常时

  • 在生产环境中监控Kafka的可靠性

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值