本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力
本文主要阐述一下在开发过程中可能出现的 kafka 消息投递中的丢失问题。
如何检测?
如何检测这个问题,其实反倒是没什么人说。
首先消息丢失可能出现在多个环节,而且也只有到消费的阶段,通过业务的感知,才好知道确实是消息丢失造成了业务逻辑失败。
其次是如何感知?也就是如何在业务执行的过程中尽早的暴露?
- 在链路追踪系统里面,可以查询到消息的生产 → 投递的全流程。如果发现流程中某个环节失败,在链路上直接上报,最终在页面上可以显示
那么在没有 链路追踪系统 如何处理?
可以通过消费 msg 在存储上的连续性来验证是否有消息丢失
这个很好理解:生产者在 send(msg) 后,通过缓冲区发送到 server → 存储是按照先后顺序存储的。
所以在 consumer 可以利用这个性质做丢失检测。
当然,有人会问如果一批消息被打到不同分区,不同分区中的 msg 不是连续的,这个如何检测?
首先,要明确只有在 one partition 中才是有序的,你需要保证的也只是 one partition 中的消息连续性。 所以,步骤应该是:
- 按照指定分区策略 → msg 分区
- 进入逻辑分区的 msg,附加一个递增的序号,并加上该分区的标示
- 这样在消费 one partition 时,记录 lastmsgId,用于下一次 msgId 的比较
当没有消息丢失的时候,msgId 在逻辑上是连续的。如果出现不连续,则可以上报打点 → promethus,监控分区的消费丢失情况。然后根据 业务情况,对分区丢失情况做 alert。具体的步骤:
- 在 consumer 拦截器中,检测记录的 lastmsgId 是否和当前的 msgId 连续
- 不连续 → 上报当前的 partition 以及 lastmsgId 到 promethus
- 对分区丢失情况做 alert
消息丢失
首先问自己,可能丢失的阶段有哪些?
- producer 丢失
- consumer 丢失
- kafka server 丢失
清楚了阶段,就来想想,如何解决丢失?
producer
一般来说,producer 发送msg使用 → producer.send(msg) 。这种情况下,开发者发送消息出去,并不知道 msg 是否发送成功:
- 发送是异步的 ⇒ client 端有缓冲区 →
RecordBatch - 实际意义上的不知道是不是发送成功
此处可以使用:producer.send(msg, callback) 。
因为既然异步处理,回调的作用就只是在操作完毕后,做一些确认或者是失败补偿机制啥的。
另外就是:重试机制 ⇒ 可以将 retries 设置为较大的值(一般为3),同时注意重试间隔(退避算法啥的)。因为无效的重试是没有意义的,浪费资源。
consumer
这个阶段的丢失其实是:消费者没有真正消费成功这条 msg。如何评定 consumer 消费成功 msg?
就是向 server 发送 commit offset 的请求,server 认定你已经消费了(不然你提交干嘛,闹着玩呢?),但是 consumer 实际上没有对 msg 做处理或者是消费逻辑失败。
解决方案 :auto.commit = true ⇒ auto.commit = false
但是紧接出现的问题是:重复消费,以至于如何保证消费幂等的问题?
先按下不表,后面来说这个大块的问题
server
本质来说,如果说是 server 端出问题,一般是副本复制机制没有做到位。
你在单节点情况下,出现了什么 server 宕机;或者是多节点下,同步没有到位,都会有可能发生消息丢失的情况。
这种情况,通过调参解决。涉及 4 个参数:
acks = allreplication.factor ≥ 3min.insync.replicas > 1unclean.leader.election.enable = false
对于 1 :看开发情况,是否需要全部副本都接收到 msg
对于2/3:replication.factor = min.insync.replicas + 1 不能说一个副本挂了,导致整个集群无法正常工作,不符合高可用。
对于 4:当 leader 副本发生故障时就不会从 follower 副本中和 leader 同步程度达不到要求的副本中选择出 leader ,这样降低了消息丢失的可能性。