为什么是Kafka

为什么是 Kafka

消息队列是一种中间件技术,用来实现组件解耦,流量消峰,消息异步等功能。通俗理解,他就是一个大池子,池子里的资源被生产者生产,被消费者消费。和消息相关的叫客户端,做资源管理的叫服务端。

我们使用一个技术前,要了解该技术的特性,熟悉他是如何解决我们所关心的问题的。

消息中间件通常号称高可靠,高吞吐率。我们来分析一下,当然是 Kafka 为例:

1,高可靠

1.1,消息可靠性保证

消息可靠性的保证级别:

  • 最多一次,at most once,消息可能会丢失,但不会被重复发送
  • 至少一次,at least once,消息不会丢失,但可能会被重复发送
  • 精确一次,exactly once,消息不会丢失,也不会被重复发送。

kafka 的消息可靠性保证级别是第二种,即消息不能丢失是重要的,允许重复发送的情况产生。但只要保证业务幂等性(消费多次的结果和消费一次是一样的),那么 kafka 也能实现精确一次。

其实,kafka 提供了幂等性 Producer,enable.idempotence,可以保证单会话单分区上不会存在重复消息。(原理大概是通过字段识别,自动去重);

还提供了事务型 Producer,保证跨分区、跨会话间的幂等性。(性能差)isolation.level ,可以设置读未提交,和读已提交的隔离级别。这两种方式保证的是 produce 不会向 broker 发送重复消息,但是由于 consumer 在偏移量提交和消息处理顺序的延后问题,仍然可能导致消息被重复消费或者是消息丢失消费。

消息高可靠需要从下面三个方面思考:

  • 生产者丢失数据:通过 confirm 机制(异步),消息队列写入成功后回传一个 ack;失败后回调 nack 生产者继续发送。还有一个同步的思路,开启事务(rabbitMQ)
  • 消息队列丢失数据:kafka 来说 partion 多副本机制;ack=-1;设置更大的重试次数;min.insync.replicas 最少的复本量表示提交完成。
  • 消费者丢失数据:(关闭 rabbitMQ 自动 ack)关闭 kafka 自动提交 offset;手动提交,重复消费问题要由业务幂等性解决。

ACK 机制

为保证 producer 发送的数据,能可靠的发送到指定的 topic,topic 的每个 partition 收到 producer 发送的数据后,都需要向 producer 发送 ack(acknowledgement),如果 producer 收到 ack,就会进行下一轮的发送,否则重新发送数据。

收到 ack 的数量有 acks 参数控制:0,表示不收到就应答;1 表示收到就应答;-1(默认)表示全部收到才应答。

这里 partition 确认收到,是在 follwer 和 leader 完成同步之后才确认的,确保 leader 挂了之后,follwer 能够选举出新的 leader。而且默认采用的是全部同步完成再发送 ACK 的机制。

但是这里有一个问题,当某个 follwer 故障,leader 便迟迟不能同步完成,为此 Leader 维护了一个 ISR(in-sync replica set),在这个集合里是保持和 leader 同步的 follwer(相应的其他不同步的是 OSR,一个 partition 的所有副本不做 leader 和 follower 区别时叫做 AR)。当 ISR 中的 follower 完成数据的同步之后,leader 就会给 follower 发送 ack。如果 follower 长时间 未 向 leader 同 步 数 据 , 则 该 follower 将 被 踢 出 ISR,时间阈值可由 replica.lag.time.max.ms 设置,而且 leader 的选取是从 ISR 中选择的。(ISR 是完全同步的一种优化机制)。可以看到,度量同步的机制,是由时差决定的,即最多允许 follewer 落后 leader 多少时间,而不是数据。即便某个 follwer 保存更多的数据,也会被移除。

当我们的 ISR 为空怎么办?这时候就要开启 Unclean 选取了,即在 OSR 中选取 leader。不开启的话保证了数据一致性但牺牲了可用性;开启的话,保证了可用性但牺牲了一致性。CAP 理论:一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)自能取其二。CA 间抉择。一般不开启,我们要保证一致性。

副本机制

领导者副本(Leader Replica)和追随者副本(Follower Replica)。每个分区在创建时都要选举一个副本,称为领导者副本,其余的副本自动称为追随者副本。和其他中间件设计方式不同,kafka 中的 follower 不对外进行提供服务,他唯一的任务就是向领导者副本异步拉取消息,然后写入到自己的提交日志。(为什么?好处?坏处?)

坏处:不能像 mysq 那样读写分离,为主服务器分担读压力;也不能基于地理位置改善数据局部性

好处:写可见的及时性;实现单调读一致性。

副本之间是如何完成同步的?高水位机制。

由于我们并不能保证 Kafka 集群中每时每刻 follower 的长度都和 leader 一致(即数据同步是有时延的),那么当 leader 挂掉选举某个 follower 为新的 leader 的时候(原先挂掉的 leader 恢复了成为了 follower),可能会出现 leader 的数据比 follower 还少的情况。为了解决这种数据量不一致带来的混乱情况,Kafka 提出了以下概念:

  • LEO(Log End Offset):指的是每个副本最后一个 offset;
  • HW(High Wather):指的是消费者能见到的最大的 offset,ISR 队列中最小的 LEO。

消费者和 leader 通信时,只能消费 HW 之前的数据,HW 之后的数据对消费者不可见。

  • 当 follower 发生故障时:follower 发生故障后会被临时踢出 ISR,待该 follower 恢复后,follower 会读取本地磁盘记录的上次的 HW,并将 log 文件高于 HW 的部分截取掉,从 HW 开始向 leader 进行同步。等该 follower 的 LEO 大于等于该 Partition 的 HW,即 follower 追上 leader 之后,就可以重新加入 ISR 了。
  • 当 leader 发生故障时:leader 发生故障之后,会从 ISR 中选出一个新的 leader,之后,为保证多个副本之间的数据一致性,其余的 follower 会先将各自的 log 文件高于 HW 的部分截掉,然后从新的 leader 同步数据。

HW 规则只能保证副本之间的数据一致性!

1.2,消息顺序性保证

  • 生产者写入乱序

首先多分区之间的消息有序性保证,通过分区 key 实现,同一消息发送到同一 partion 中。

单分区的内部消息的有序性保证,思路一样是通过序列号机制 <Producer ID,Sequence Number>,服务端只会按照序列号递增的原则接受消息的写入,从而避免因为重试机制导致的消息顺序错乱问题。

  • 消费者消费乱序

消费者大部分可能因为下游多线程处理造成消息消费顺序错乱,这是自身业务的问题,服务端无法保证。

如何解决?基于 key 或基于 hash,将相同 key 的数据放入同一队列中,指定线程消费指定队列即可。

2,高吞吐率

Apache Kafka 基准测试:每秒写入 2 百万(在三台廉价机器上)

顺序读写

kafka 基于廉价的磁盘进行数据落库,普通磁盘不擅长随机 IO,但是顺序 IO 一样可以很快。而且,他利用了操作系统的缓存页技术提高 IO 效率,page cache,也就是说数据先写入 page chche,然后再由操作系统在某个时刻进行刷盘,节省了数据由用户空间拷贝到内核空间的过程。但是这样做的缺点是数据不可靠,因为数据并没有真正落盘。kafka 提供了一个参数 producer.type 控制写入策略:同步写,写之后立即 flush,异步写。

零拷贝

概念上就是直接在内核空间将数据从磁盘拷贝到网卡,避免了传统 IO 方式中数据在内核空间和用户空间多次拷贝的过程。通过零拷贝技术,就不需要把 内核空间页缓存里的数据拷贝到应用层缓存,再从应用层缓存拷贝到 Socket 缓存了,两次拷贝都省略了,所以叫做零拷贝。

在操作系统层面由 sendfile 这个 systemm ctl 实现。

数据压缩

批量压缩,提升 IO 能力;多种压缩协议:snappy,gzip,zstad

参考链接

极客时间:Kafka 核心技术与实战

Kafka 零拷贝技术:https://www.jianshu.com/p/0af1b4f1e164

猜你喜欢

转载自blog.csdn.net/qq_40589204/article/details/124528799