怎么让kafka变得可靠 前言 Broker 生产者 消费者

前言

数据投递可靠性指当给生产者回复“投递成功”时,消息一定不会丢失

kafka可以应用在多种场合里,有些场合注重更高的可靠性,例如银行业务,有些则不然,例如记录日志。因此kafka提供各种配置来满足不同程度的可靠性需求

可靠性不是单一组件的事,而是整个系统层面的概念,因此要保证kafka的可靠性,需要集群运维人员,应用开发人员一起努力,才能构建一个可靠的系统

当然原则上基础服务的可靠性尽量由基础架构团队保障,业务团队更专注业务本身

下面依次从broker,生产者和消费者来介绍如何保证kafka的可靠性

Broker

kafka中每个topic中,每个分区的多副本机制是保障数据可靠性的核心。试想如果只有一个副本,则其发生崩溃时就是造成数据丢失,而如果有多个副本,则只要有一个副本可用,数据就不会丢失

复制系数

复制系数(replication.factor)即每个分区的副本个数,默认是3。

默认值一般够用,当然也可以往上加,复制系数越高代表更高的可靠性与可用性,同时也意味着更高的存储成本,这是可靠性与硬件成本之间的权衡

如果不需要太高的可靠性,可将该值设为1,即每个分区只有一个副本。这样不仅影响可靠性,同时也会影响可用性:当唯一的副本所在broker宕机时,由于没有其他副本出来顶上,会造成一段时间内不可用

多个副本之间的分布情况是值得考虑的点,kafka默认会把分区的每个副本尽量分配到不同的broker上,这样能保证实例层面的可靠性

在部署时可以将broker分布在不同的机架上,并使用 broker.rack参数来配置机架名。kafka能保证分区的每个副本尽量在不同的机架上,进一步提高数据可靠性

因此比较好的分区分配方式如下:

流程图3 (1).jpg

其实可靠性还可以往上加,在机房,城市,国家层面容灾,不过这就是通用的容灾手段,本文不进行考虑

最少同步副本

如果一个分区有3个副本,但可能只有一个同步副本

同步副本:最近向主副本获取过消息,且获取的消息是最新的,则称该副本是同步的。一个副本如果不同步,可能因为已经崩溃,和主副本无联系,或还有联系,但请求的数据落后较多

可通过参数 min.insync.replicas设置最小同步副本。例如对于一个3个副本的分区,若该参数设为2,那至少存在2个同步副本,才能向该分区写入数据,否则生产者会受到 NotEnoughReplicasException 异常

那这个参数有啥用呢?假设最小同步副本为1,即容许只有1个同步副本。

消息只有在被写入所有同步副本时,才被认为是已提交的,这里的“所有副本”只有1个。若此时该副本所在的broker宕机,且还没复制到其他副本(其他副本不是同步副本,可能没有追上主副本最新数据),就会发生数据丢失

即当broker给生产者回复了“提交成功”,但数据还是丢了,丧失数据可靠性

如果设为2或3,则将提升数据可靠性,同时会带来响应时间增大的缺点

生产者

我们把broker端配置得很可靠,但如果没有生产者和消费者在可靠性方面进行相应的配置和处理,数据也会变得不可靠

在发送者端,我们需要做的是

  • 正确配置发送确认
  • 正确处理发送错误

发送确认

发送确认(acks)有以下三种配置选项

  • acks=0
+ 只要生产者通过**网络**将消息发出去,则认为发送成功。在发出去之前的操作(序列化,选择分分区,网卡故障)如果失败会提示到生产者端,但如果遇到分区离线等broker端的异常,发送端无法感知,也就造成了数据丢失
+ 该模式一般用来压测性能,生成环境中不建议使用
  • acks=1
+ 将消息写入分区的主副本时,才会返回写入成功或失败。该模式比acks=0有更高的可靠性,但也有概率发生数据丢失,即主副本没来得及将数据同步到其他副本前发生崩溃
  • acks=all
+ 将消息写入该分区的所有同步副本时,才会返回写入成功或失败。至于写入多少个副本,根据该分区中当前有多少个同步副本决定,可结合broker端的最小同步副本使用,即至少写入 min.insync.replicas个副本,broker端才给生产者端响应“写入成功”
+ 该模式能最大限度保障可靠性,但会带来吞吐量降低的缺点,需根据业务场景对可靠性的要求进行选择

处理错误

生产者发送消息的错误分为两类,可重试和不可重试

  • 可重试
+ 例如正在选举主分区的 LEADER\_NOT\_AVAILABLE(主分区不可用)错误,或网络抖动导致的连接失败错误,该类错误可以通过重试来解决
+ 需要注意总重试时间((重试次数-1)\* 每次重试直接的间隔)要比选举过程长,否则生产者会过早放弃重试
  • 不可重试:
+ 有些错误无法通过重试来解决,即无论重试多少次都会失败,例如消息过大错误

可通过retries配置重试次数,通过retry.backoff.ms配置重试间隔,生产者客户端会自动处理可重试错误,而程序代码中需要处理不可重试错误,和超过重试次数上限的错误,一般是记录到日志或保存到数据库里,依据业务判断是直接忽略还是人工接入处理

需要注意的是,重试可能带来消息重复的问题,例如:生产者因为网络问题没有收到 broker 的确认,但实际上消息已经写入成功,生产者却不知道,认为网络或broker出现了临时故障,就重试发送该消息。此时消费者端需要考虑消息处理的幂等性

消费者

作为消费者来说,需要做到不漏处理消息,在此基础上尽量不重复处理消息

因此消费者需要跟踪哪些消息是处理过的,哪些还没有处理

消费者的处理流程为:从broker获取一批数据,进行处理,提交偏移量

为什么需要提交偏移量?如果一个消费者退出(可能是宕机或重启),另一个消费者来接手(或“再均衡”)该分区时,需要知道上一个消费者已经消费到哪了。这里“另一个消费者”也可能是重启后的自己

因此这里提交偏移量的时间就比较关键,如果消费者在没有处理完消息前,就提交了偏移量,就会造成数据丢失,如果提交偏移量比较保守,会造成重复处理消息

重复消费:

流程图1.jpg

消息丢失:、

流程图2.jpg

提交偏移量

提交偏移量有自动·和手动两种方式:

  • 自动提交偏移量

可通过配置 enable.auto.commit=true来开启自动提交偏移量

其提交时机为,每次拉去消息时,且距离上次提交偏移量经过了提交间隔(auto.commit.interval.ms)的时间,就会自动提交上一次拉去的最大偏移量

自动提交的优缺点为:

  • 优点:
+ 方便,开发者可以少考虑偏移量提交
+ 若程序保证在新一次拉去之前的消息都被处理完毕,则自动提交能保证消息不会丢失
  • 缺点:
+ 如果提交间隔过长,可能导致实际处理的偏移量比提交的偏移量大很多,若发生消费者切换,会导致消息重复处理
+ 不能异步处理消息(破坏**在新一次拉去之前的消息都被处理完毕**条件),否则会造成数据丢失
  • 手动提交偏移量

若希望更多地控制提交时机,则需要手动控制偏移量提交,若在每次处理完一批消息都就提交,可以减少处理重复消息的可能性,但无法避免(若刚处理完就宕机,则一定会重复处理该条消息),并且会损耗一定的吞吐量

仅处理一次

不管是生产者端的重试,还是消费者端提交偏移量,都会导致消息被重复处理,若业务上消息重复处理了也没事,则可以忽略,否则可以在消息里加上唯一id标识,处理时根据该id进行幂等性校验

总结

本文从broker,生产者,消费者的角度介绍如何提高kafka的可靠性:

  1. broker端需要配置合理的复制系数,最小同步副本数越多,数据可靠性越强
  2. 生产者端需要配置可靠的发送确认,以及正确地错误处理
  3. 消费者端需要保证提交偏移量时,该批次消息已经处理完毕

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%