Kafka学习总结

Kafka消费模式

点对点消息传递模式

在点对点系统中,消息持久化到一个队列中。此时将有一个或多个消费者,消费队列中的数据,但是一条消息最终只能被消费一次。当一个消费者消费了队列中的某条数据后,该条数据则从消息队列中删除。该模式即使有多个消费者同时消费数据,也能保证数据处理的顺序。

图片1.png

发布订阅模式

在发布订阅模式中,消息被持久化到一个topic中。与点对点消息系统不同的是,消费者可以订阅一个或者多个topic,消费者可以消费该topic中所有的数据,同时一条数据可以被多个订阅此tpic的消费者消费,数据被消费后不会立马删除。

图片1.png

push vs. pull

Kafka 在这方面采取了一种较为传统的设计方式,也是大多数的消息系统所共享的方式:即producer 把数据 push 到 broker,然后 consumer 从 broker 中 pull 数据。 

push-based 系统必须选择适合时机例如:立即发送请求或者积累一定数量的数据,然后在不知道下游的 consumer 能否立即处理它的情况下发送这些数据。而 pull-based 的模式下速率可以根据 consumer 的处理能力来决定,进而使得数据能够得到最佳的处理时机,而不会引入不必要的延迟。

如果 broker 中没有数据,consumer 可能会进去循环中轮询,但我们在 pull 请求中加入参数,使得 consumer 在一个“long pull”中阻塞等待,直到数据到来

Kafka的名词解释

streams-and-tables-p1_p4.png

broker

kafka集群中包含一个或多个服务器,服务器的节点称为broker,broker存储topic的数据。

  • 如果某topic有N个partition,集群有N个broker,那么每个broker存储该topic的一个partition。
  • 如果某topic有N个partition,集群中broker数目少于N个,那么一个broker存储该topic的一个或多个partition。实际生产环境中,尽量避免这种情况的发生,这种情况容易导致kafka集群数据的不均衡

topic

每条发布到kafka集群的消息都有一个类别,这个类别称为topic(物理上不同topic的消息分开存储,逻辑上一个topic的消息虽然保存于一个或者多个broker上但用户只需指定消息的topic即可生产或消费数据而不需要关心数据存于何处)类似数据库的表明

partition(分区)

topic中的数据被分割为一个或多个partition,每个topic至少有一个partition,每个partition中的数据使用多个segment文件存储。

partition中的数据是有序的,但是如果划分多个partition的话这时候不能保证数据的有序,如果需要保证严格的顺序消费,需要将partition的数目设置为1。

producer

生产者就是数据发送者,该角色将消息发送到kafka的topic中。broker接受到生产者发送的消息后,broker将消息追加到当前用于追加数据的segment文件中。生产者发送消息,存储到一个partition中,当然生产者也可以指定数据存储的partition。

cunsumer

消费者可以从broker中读取数据,也可以消费多个topic里面的数据。

每个Consumer都属于一个特定的Consumer Group,可以为每个Consumer指定group name,若不指定group name则属于默认group。

可以把一个消费者组里面的所有消费者当做一个大的消费者团体,主要是为了提高并发,某一个partition只能被同一个消费者组里面的一个消费者消费。但是一个partition可以被多个消费者组消费。

Kafka工作流程

topic 和 partition

topic在逻辑上可以被认为是一个queue,每条消息必须指定他的topic。为了使kafka吞吐量可以线性提高,物理上把topic分成一个或多个partition,每个partition再物理上对应一个文件夹,该文件夹下存储这个partition的所有消息和索引文件。

创建一个topic时同时可以指定分区数目,分区数目越多,其吞吐量也越大,但是需要的资源也就越多。kafka再接收到生产者发送的消息后,会根据负载均衡策略将消息存储到不同的分区中。因为每条消息都被append到该partition中,属于顺序写磁盘,因此效率非常高。

图片1.png

对于传统的消息队列而言,一般会删除已经被消费的消息,而kafka集群会保留所有的消息,无论其被消费与否。当然因为磁盘的限制,不可能永久保留所有数据,因此kafka提供两种策略删除旧数据:一是基于时间,二是基于partition文件大小。因为kafka读取特定消息的时间复杂度为O(1),所以删除过期文件与提高kafka的性能无关,只与磁盘和具体的需求有关。

另外,kafka会为每一个consumer group保留一些元信息,记录当前消息的position也就是offset。正常情况下Consumer会在消费完一条消息会递增该offset。当然如果把consumer的offset重新设置一个较小的值,就可以重新消费一些信息。因为offset由consumer控制,所以kafka的broker是无状态的,它不需要标记哪些消息被哪些消费过,也不需要通过broker去保证同一个consumer group只有一个consumer能够消费某一条消息,因此也就不需要锁机制,这也是为kafka的高吞吐量提供了有力保证。

producer消息路由

producer发送消息到broker时,会根据partition机制选择将其存储到哪一个partition。如果partition进制设置合理,所有消息可以均匀分布到不同的partition里,这样就实现了负载均衡。如果一个topic对应一个文件,那这个文件所在机器的I/O将成为topic的性能瓶颈,而有了partition后,不同的消息可以并行写入不同broker的不同partition里,极大的提高了吞吐量。

发送一条消息时,可以指定这条消息的key,producer根据这个key和partition机制来判断应该将这条消息发送到那个partition。

Kafka delivery guarantee消息传递机制

有这么几种可能的delivery guarantee:

  • At most once:消息可能会丢,但绝不会重复传输
  • At least one:消息绝不会丢,但可能会重复传输
  • Exactly once:每条消息肯定会被传输一次且仅传输一次,很多时候这是用户想要的

当producer向broker发送消息时,一旦这条消息被commit,因replication存在他就不会丢失。但是如果producer发送数据给broker后,遇到网络问题而造成通讯通讯中断,那producer就无法判断该条消息是否已经commit。虽然kafka无法确定网络故障期间发生了什么,但是producer可以生成一种类似主键的东西,发生故障时重试多次,这样就做到了exactly once

consumer在从broker读取消息后,可以选择commit,该操作会在zookeeper中保存该consumer在该partition中读取的offset(0.9版本之后offset由kafka自己维护)该consumer下一次再读取partition时会从吓一跳开始读取。如未commit下一次读取的位置还是会跟上一次commit之后的开始位置相同。当然可以将consumer设置为auto commit,既consumer一旦读取到数据立即commit。如果只讨论这一读取消息的过程,那kafka是保证了exactly once。但是实际使用中,应用程序并非在consumer读取完数据就结束了,而是要进一步处理,而数据处理与commit的顺序在很大程度上决定了消息从broker和consumer的delivery guarantee semantic。

kafka默认保证了at least once,并且允许通过设置producer异步提交来实现at most once。而exactly once要求与外部存储系统写作,幸运的是kafka提供的offset可以非常直接容易的使用这种方式。

高可用

kafka 0.8以前,是没有HA机制的,就是任何一个broker宕机了,那个broker上的partition就废了,没法写也没法读,没有什么高可用性可言。kafka 0.8以后,提供了HA机制,就是replica副本机制。每个partition的数据都会同步到其他机器上,形成自己的多个replica副本。然后所有replica会选举一个leader出来,那么生产和消费都跟这个leader打交道,然后其他replica就是follower。

写的时候,leader会负责把数据同步到所有follower上去,读的时候就直接读leader上数据即可。kafka会均匀的将一个partition的所有replica分布在不同的机器上,这样才可以提高容错性。

这么搞,就有所谓的高可用性了,因为如果某个broker宕机了,那个broker上面的partition在其他机器上都有副本的,如果这上面有某个partition的leader,那么此时会重新选举一个新的leader出来,大家继续读写那个新的leader即可。这就有所谓的高可用性了。

读写规则

写数据的时候,生产者就写leader,然后leader将数据落地写本地磁盘,接着其他follower自己主动从leader来pull数据。一旦所有follower同步好数据了,就会发送ack给leader,leader收到所有follower的ack之后,就会返回写成功的消息给生产者。

消费的时候,只会从leader去读,但是只有一个消息已经被所有follower都同步成功返回ack的时候,这个消息才会被消费者读到。

如何保证消息可靠性

kafka自己把消息搞丢了

当生产者往kafka写了1条数据,当partition的leader还没有把消息同步给follower的时候leader宕机了。然而kafka选举了其中一台还未同步消息的follower成为了leader。这时候这条消息就相当于丢失了。

一般情况下想避免因为kafka自身原因而造成的消息丢失,我们只需要配置下面几个参数就可以了:

  1. topic设置replication.factor参数值必须大于1,意思就是每个partition的leader必须至少有2个副本。
  2. 设置min.insync.replicas必须大于1,要求leader至少感知到1个follower还跟自己保持联系,没掉队。这样才能确保leader挂了还有一个follower。
  3. producer端设置acks=all,要求每条数据写入所有replica之后,才能认为是写入成功了。
  4. producer端设置retries=MAX,这个配置要求一旦写入失败,无限重试。

生产者丢失数据

如果按照上述思路设置了acks=all,那么生产者就一定不会丢失数据,因为它邀请你的leader接收到消息,所有的follower都同步到了消息之后,才认为本次写成功了。如果没有满足这个条件,生产者会不断重试。

消费端丢失消息

唯一可能导致消费者丢失数据的情况就是,消费者收到了消息然后自动提交了offset,kafka误以为已经消费了这个消息,其实才刚要处理这个消息。但是处理过程中发生了异常或者消费端发生宕机。解决方案就是手动提交offset。

顺序消息

当kafka架构是1个topic,多个partition这时候明显就无法保证消息的有序性了。但是写入一个partition的消息是有顺序的,所以创建kafka的时候规定topic只有一个partition,消费者从partition中取数据一定是按照顺序取的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值