【DevDojo】Kafka实操 ·一文快速搞懂kafka基本术语概念

【DevDojo】
@you: “Stay focused and work hard!
@you: Each partition has one server(broker) which acts as the “leader” and zero or more servers which act as “followers”
在这里插入图片描述

02.1 Kafka 的三层消息架构(消息层次)?

Kafka 属于分布式的消息引擎系统,它的主要功能是提供一套完备的消息发布与订阅解决方案。在 Kafka 中,发布订阅的对象是主题(Topic),你可以为每个业务、每个应用甚至是每类数据都创建专属的主题。

向主题发布消息的客户端应用程序称为生产者(Producer),生产者程序通常持续不断地向一个或多个主题发送消息,而订阅这些主题消息的客户端应用程序就被称为消费者(Consumer)。和生产者类似,消费者也能够同时订阅多个主题的消息。**我们把生产者和消费者统称为客户端(Clients)。**你可以同时运行多个生产者和消费者实例,这些实例会不断地向 Kafka 集群中的多个主题生产和消费消息。

有客户端自然也就有服务器端。Kafka 的服务器端由被称为 Broker 的服务进程构成,即一个 Kafka 集群由多个 Broker 组成,Broker 负责接收和处理客户端发送过来的请求,以及对消息进行持久化。

虽然多个 Broker 进程能够运行在同一台机器上,但更常见的做法是将不同的 Broker 分散运行在不同的机器上,这样如果集群中某一台机器宕机,即使在它上面运行的所有 Broker 进程都挂掉了,其他机器上的 Broker 也依然能够对外提供服务。这其实就是 Kafka 提供高可用的手段之一。

实现高可用的另一个手段就是备份机制(Replication)。备份的思想很简单,就是把相同的数据拷贝到多台机器上,而这些相同的数据拷贝在 Kafka 中被称为副本(Replica)。Kafka 定义了两类副本:领导者副本(Leader Replica)和追随者副本(Follower Replica)。前者对外提供服务,这里的对外指的是与客户端程序进行交互;而后者只是被动地追随领导者副本而已,不能与外界进行交互。当然了,你可能知道在很多其他系统中追随者副本是可以对外提供服务的,比如 MySQL 的从库是可以处理读操作的,但是在 Kafka 中追随者副本不会对外提供服务。

副本的工作机制也很简单:生产者总是向领导者副本写消息;而消费者总是从领导者副本pull拉取读消息。至于追随者副本,它只做一件事:向领导者副本发送Pull请求,请求领导者把最新生产的消息发给它,这样它能保持与领导者的同步。

副本(Replica)? 好吧,其实在整个分布式系统里好像都叫这个名字。副本的数量是可以配置的,这些副本保存着相同的数据,但却有不同的角色和作用。一个有意思的事情是现在已经不提倡使用 Master-Slave 来指代这种主从关系了,毕竟 Slave 有奴隶的意思,在美国这种严禁种族歧视的国度,这种表述有点政治不正确了,所以目前大部分的系统都改成 Leader-Follower 了。

虽然有了副本机制可以保证数据的持久化或消息不丢失,但没有解决伸缩性的问题。伸缩性即所谓的 Scalability,是分布式系统中非常重要且必须要谨慎对待的问题。什么是伸缩性呢?我们拿副本来说,虽然现在有了领导者副本和追随者副本,但**倘若领导者副本积累了太多的数据以至于单台 Broker 机器都无法容纳了,此时应该怎么办呢?一个很自然的想法就是,能否把数据分割成多份保存在不同的 Broker 上?**如果你就是这么想的,那么恭喜你,Kafka 就是这么设计的。

这种机制就是所谓的分区(Partitioning)

Kafka 中的分区机制指的是将每个主题划分成多个分区(Partition),每个分区是一组有序的消息日志。生产者生产的每条消息只会被发送到一个分区中,也就是说如果向一个双分区的主题发送一条消息,这条消息要么在分区 0 中,要么在分区 1 中。

如果你了解其他分布式系统,你可能听说过分片、分区域等提法,比如 MongoDB 和 Elasticsearch 中的 Sharding、HBase 中的 Region,其实它们都是相同的原理,只是 Partitioning 是最标准的名称。

如你所见,Kafka 的分区编号是从 0 开始的,如果 Topic 有 100 个分区,那么它们的分区号就是从 0 到 99。刚才提到的副本如何与这里的分区联系在一起呢?实际上,副本是在分区这个层级定义的。每个分区下可以配置若干个副本,其中只能有 1 个领导者副本和 N-1 个追随者副本。生产者向分区写入消息,每条消息在分区中的位置信息由一个叫位移(Offset)的数据来表征。分区位移总是从 0 开始,假设一个生产者向一个空分区写入了 10 条消息,那么这 10 条消息的位移依次是 0、1、2、…、9。

至此我们能够完整地串联起 Kafka 的三层消息架构:

  • 第一层是主题层,每个主题可以配置 M 个分区,而每个分区又可以配置 N 个副本。
  • 第二层是分区层,每个分区的 N 个副本中只能有一个充当领导者角色,对外提供服务;其他 N-1 个副本是追随者副本,只是提供数据冗余之用。
  • 第三层是消息层,分区中包含若干条消息,每条消息的位移从 0 开始,依次递增。
  • 最后,客户端程序一次只能与某个主题的某个分区的里领导者副本进行交互。

02.2 Kafka Broker 是如何持久化数据的?

总的来说,Kafka 使用消息日志(Log)来保存数据,一个日志就是磁盘上一个只能追加写(Append-only)消息的物理文件。因为只能追加写入,故避免了缓慢的随机 I/O 操作改为性能较好的顺序 I/O 写操作,这也是实现 Kafka 高吞吐量特性的一个重要手段。

不过如果你不停地向一个日志写入消息,最终也会耗尽所有的磁盘空间,因此 Kafka 必然要定期地删除消息以回收磁盘。怎么删除呢?简单来说就是通过日志段(Log Segment)机制。在 Kafka 底层,一个日志又近一步细分成多个日志段,消息被追加写到当前最新的日志段中,当写满了一个日志段后,Kafka 会自动切分出一个新的日志段,并将老的日志段封存起来。Kafka 在后台还有定时任务会定期地检查老的日志段是否能够被删除,从而实现回收磁盘空间的目的。

02.3 Kafka的消费者? 为什么要引入消费者组呢?Rebalance重平衡?

第一期中我提到过两种消息模型,即**点对点模型(Peer to Peer,P2P)**和发布订阅模型。这里面的点对点指的是同一条消息只能被下游的一个消费者消费,其他消费者则不能染指。在 Kafka 中实现这种 P2P 模型的方法就是引入了消费者组(Consumer Group)。

所谓的消费者组,指的是多个消费者实例共同组成一个组来消费一组主题。这组主题中的每个分区都只会被组内的一个消费者实例消费,其他消费者实例不能消费它。

**为什么要引入消费者组呢?主要是为了提升消费者端的吞吐量。多个消费者实例同时消费,加速整个消费端的吞吐量(TPS)。**我会在专栏的后面详细介绍消费者组机制,所以现在你只需要了解消费者组是做什么的即可。另外这里的消费者实例可以是运行消费者应用的进程,也可以是一个线程,它们都称为一个消费者实例(Consumer Instance)。

消费者组里面的所有消费者实例不仅“瓜分”订阅主题的数据,而且更酷的是它们还能彼此协助。

重平衡:Rebalance。消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。

假设组内某个实例挂掉了,Kafka 能够自动检测到,然后把这个 Failed 实例之前负责的分区转移给其他活着的消费者。这个过程就是 Kafka 中大名鼎鼎的“重平衡”(Rebalance)。嗯,其实既是大名鼎鼎,也是臭名昭著,因为由重平衡引发的消费者问题比比皆是。事实上,目前很多重平衡的 Bug 社区都无力解决。

每个消费者在消费消息的过程中必然需要有个字段记录它当前消费到了分区的哪个位置上,这个字段就是消费者位移(Consumer Offset)。

注意,这和上面所说的位移完全不是一个概念。上面的“位移”表征的是分区内的消息位置,它是不变的,即一旦消息被成功写入到一个分区上,它的位移值就是固定的了。而消费者位移则不同,它可能是随时变化的,毕竟它是消费者消费进度的指示器嘛。另外每个消费者有着自己的消费者位移,因此一定要区分这两类位移的区别。我个人把消息在分区中的位移称为分区位移,而把消费者端的位移称为消费者位移。

02.4 为什么 Kafka 不像 MySQL 那样允许追随者副本对外提供读服务?

如果允许follower副本对外提供读服务(主写从读, 读写分离是应对度多写少, 那么消息中间件显然不是这个场景。而且mysql其实主从节点之间也会存在主从延迟,数据不一致的问题,针对一些要求数据强实时性,强一致性的业务来说,最好不要使用读写分离的方案。

首先会存在数据一致性的问题,消息从主节点同步到从节点需要时间,可能造成主从节点的数据不一致(数据一致性的问题,如果允许从follower节点读取数据,那么因为主从延迟使得无法快速获取数据,另外还会导致即使在一个partition中的数据也无法保证顺序性, 因为有__consumer_offsets的机制,每次读写都应该是在leader上,如果replica也参与了read服务,那么consumer offet就乱了)。主写从读无非就是为了减轻leader节点的压力,将读请求的负载均衡到follower节点。

如果Kafka的分区相对均匀地分散到各个broker上,同样可以达到负载均衡的效果,没必要刻意实现主写从读增加代码实现的复杂程度(因为mysql一般部署在不同的机器上一台机器读写会遇到瓶颈,Kafka中的领导者副本一般均匀分布在不同的broker中,已经起到了负载的作用。即:同一个topic的已经通过分区的形式负载到不同的broker上了,读写的时候针对的领导者副本,但是量相比mysql一个还实例少太多,个人觉得没有必要在提供度读服务了。如果量大还可以使用更多的副本,让每一个副本本身都不太大).

一个分区下面的各个replica尽量部署到不同的broker(因为不同的topic存储时是可以做到物理隔离的, 我们可以控制消息保存在指定的分区以及分区保存在哪些个broker上)。如果有两个replica部署到同一个broker,broker挂了,这两个replica就都丢失了。

分区可以实现Scalability(可扩展性),增加分区,增加了消息存储量,一个分区对应一个领导者副本,通过多个分区,可以实现负载均衡。

https://www.zhihu.com/question/327925275/answer/705690755

1,kafka的分区已经让读是从多个broker读从而负载均衡,不是MySQL的主从,压力都在主上;

2,kafka保存的数据和数据库的性质有实质的区别就是数据具有消费的概念,是流数据,kafka是消息队列,所以消费需要位移,而数据库是实体数据不存在这个概念,如果从kafka的follower读,消费端offset控制更复杂;

3,生产者来说,kafka可以通过配置来控制是否等待follower对消息确认的,如果从上面读,也需要所有的follower都确认了才可以回复生产者,造成性能下降,如果follower出问题了也不好处理

总结的挺好,但第3点说的不准确,因为无论follower是否与leader的消息完全同步,当follower提供读服务时,如果follower挂了则必然需要转移消费者的offset,这样增加了复杂度且收益也不大。

follower是主动请求leader同步数据的,这种情况要保证数据的完整性,势必要跟同步策略中的 ack=-1的情况一样,要等同步完成,才能提供服务。想象一下海量数据的情况下,leader下面有n个follower,如果都同步完成,那会有多慢。而且这种慢是双向的,生产者等消息发送成功,消费者等消息同步成功。这样“削峰填谷”就不存在了。 而且,follower提供服务完全没必要,因为一个分区只能被一个消费者消费。

02.5 总结: Each partition has one server(broker) which acts as the “leader” and zero or more servers which act as “followers”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xZovwrx5-1678850153934)(./assets/image-20230312170620127.png)]

表格:

概念理解
Kafka体系架构M个producer +N个broker(+ZK集群) +K个consumer
Broker服务代理节点,Kafka服务实例。N个broker组成一个Kafka集群, 通常一台机器部署一个Kafka实例,一个实例挂了其他实例仍可以使用,体现了高可用;
如果broker ip变化, kafka2.1开始支持客户端的dns解析, 方法是设置客户端的client.dns.lookup, 这样生产者客户端不需要修改broker的列表
消息Record。Kafka 是消息引擎嘛,这里的消息就是指 Kafka 处理的主要对象。
主题Topic。主题是承载消息的逻辑容器,在实际使用中多用来区分具体的业务。
分区Partition。一个有序不变的消息序列。每个主题下可以有多个分区。一个topic 可以拥有若干个partition(从 0 开始标识partition ),分布在不同的broker 上, 实现发布与订阅时负载均衡。producer 通过自定义的规则将消息发送到对应topic 下某个partition,以offset标识一条消息在一个partition的唯一性(一个主题三个分区,那数据只会在其中一个分区。 同时,分区位置只增加,所以唯一)。 一个partition拥有多个replica,提高容灾能力。 partition在机器磁盘上以log 体现,采用顺序追加日志的方式添加新消息、实现高吞吐量
注意上图, 同一个个主题下的分区, 有的在同一个broker, 有的在不同的broker(期望的结果)
另外, 同一个分区的不同副本必然在不同的broker上;
消息offset的唯一性仅在单个分区内, 所有的partition db都有这种问题, 无法保证全局顺序唯一的, 除非牺牲负载均衡能力去使用单分区可以解决, 即单分区才能保证消费段按顺序去读取的, 比如在直播领域,要求同一个房间内的各种行为消息(进房、离房、关注等)是有序的,那完全可以根据直播id进行分区(指定key让他达到相同的分区partition上面),保证相同的直播id的消息全部落到同一个分区,进而保证消息的有序性;
如果producer指定了要发送的目标分区,消息自然是去到那个分区;否则就按照producer端参数partitioner.class指定的分区策略来定;如果你没有指定过partitioner.class,那么默认的规则是:看消息是否有key,如果有则计算key的murmur2哈希值%topic分区数;如果没有key,按照轮询的方式确定分区
消息位移Offset。表示分区中每条消息的位置信息,是一个单调递增且不变的值。
副本Replica。Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余,这些地方就是所谓的副本。副本还分为leader 副本、follower副本,leader副本负责读写请求,follower 副本负责同步leader副本消息,通过副本选举实现故障转移。副本是在分区层级下的,即每个分区可配置多个副本实现高可用。Kafka不是PUSH模型,而是PULL模型,即follower副本不断地从leader处异步拉取消息。最新的版本不单纯依赖高水位去判断了, 依靠leader epoch"
副本是在分区层级下的"的依据是在Kafka的源码Partition.scala中,每个分区对象都保存了一个hashmap,map中的对象就是Replica副本对象。
注意分区和副本的关系, 有多个broker才有才会有副本? **通常单broker下多分区(不要超过2000)**也比单broker下单分区的TPS来得大; 不管leader还是follower, 再broker上的分配策略大体上遵循轮询的公平法则, 而不是随机的
从follower中选择, leader的算法如下: (1) 从ISR中选择存活的第一个副本为新leader 2.2 如果ISR为空,看是否开启了unclean leader选举,如果没有开启,那么Kafka干脆就不选leader了,直接将分区置于不可用状态;否则Kafka就从剩下的存活副本中选第一个副本作为leader(这里的顺序就是ZooKeeper中保存的副本集合顺序,即assigned_replicas项)
生产者Producer。向主题发布新消息的应用程序。
客户端发送Metadata请求获取每个topic分区的leader,之后再发送真实的数据请求(Produce请求或Fetch请求)
消费者Consumer。从主题订阅新消息的应用程序。一个topic 可以让若干个consumer消费,若干个consumer组成一个 consumer group ,一条消息只能被consumer group 中一个consumer消费,若干个partition 被若干个consumer 同时消费,达到消费者高吞吐量(如果不同的consumer group的其他consumer也是可以消费这条消息的);
Kafka不能推送消息给consumer。Consumer只能不断地轮训去获取消息。从Kafka流向consumer的唯一方式就是通过poll。
消费者位移Consumer Offset。表征消费者消费进度,每个消费者都有自己的消费者位移。
消费者组Consumer Group。多个消费者实例共同组成的一个组,同时消费多个分区以实现高吞吐。
重平衡Rebalance。消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。
如果消费者挂了自然是没法提交位移,所以有重复消费的问题。Kafka一直是承认这一点的啊,即消费者端存在消息被消费多次的情况;
Now, topicA有4 partition P1&P2&P3&P4, 2 consumer C1&C2, 假设C1消费P0,P1, C2消费P2,P3。如果C1从未提交,C1挂掉,C2开始消费P0,P1,发现没有对应提交位移,那么按照C2的auto.offset.reset值决定从那里消费,如果是earliest,从P0,P1的最小位移值(可能不是0)开始消费,如果是latest,从P0, P1的最新位移值(分区高水位值)开始消费。但如果C1之前提交了位移,那么C1挂掉之后C2从C1最新一次提交的位移值开始消费。 所谓的重复消费是指,C1消费了一部分数据,还没来得及提交这部分数据的位移就挂了。C2承接过来之后会重新消费这部分数据。这种情况只能业务层自己处理了吧,在消息体中添加唯一ID,消费者消费到消息后,进行该唯一ID的校验,比如使用redis实现分布式锁。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值