消息队列-kafka入门详解

本文适用于初学者,学习kafka之前 应该都知道它是消息队列,但是和我们印象中数据结构的队列不同的是,它持久化到磁盘上。

1、我们首先从定义来看  Kafka  一个分布式的、分区化、可复制提交的日志服务,我们先来想想什么是分区,好比图书馆的书,会分成好几个区。那么kafka也是这样的,他会把消息分成几个区,0-100寸到A区,100-200 存到B区。这个专有名词就叫做partition。可复制说的就是主从节点之前的复制了。

2、那么此时你会不会有疑问,我们为什么需要中间件,为什么会用到kafka呢? 都会说:应用场景:解耦、异步、削峰

     假设一个场景:A系统需要告诉B系统,我这边某些业务处理完了,到B系统处理相关业务了,但是A系统并不关心B系统是否处理完,只是通知B系统。这时候我们就可以引入消息队列了。A系统只需要把消息扔到消息队列里就好,不需要等待B系统处理完,就可以做自己的逻辑。B系统监听到消息就可以执行自己的业务了。

目录

初识kafka

框架图

深入kafka

Kafka如何进行复制

Kafka如何处理来自生产者和消费者的请求

Kafka得存储细节


初识kafka

框架图

首先先来思考几个问题:

1.想一想为什么采用的是发布订阅模式 发布订阅模式好处

kafka采用发布订阅的模式,我们先试想一下,假如不是发布订阅模式的话,是broker去像消费者推送消息的话,举个例子,双十一,你买了很多很多快递,然后快递小哥一直把快递往你身上塞,他也不管你拿不拿得住,最后你坚持不住了,撒手不要了。 但是如果是发布订阅模式,你可以一次去快递点取5件,拿回家之后,再去快递点拿剩下5件。

所以明白为什么是发布订阅模式了嘛?消费者的消费能力只有它自己知道,这样的话 消费者可以知道以一个什么样的速度去队列中拉取消息。可以控制自己的消费速度。

2.Producer怎么知道消息被broker接收到了?

因为网络是不可靠的,生产者怎么知道 我发了一个消息,一定被消息队列接收到了呢?所以 Producer是有个ACK的应答机制的,在xml文件中可以配置,不配的话有默认的

3.消费者为什么要有分组?消费者组中的leader有什么用?

再拿取快递作为例子,消费者分组就好比你们寝室,你和室友一起去帮你拿快递,是不是速度就提高了一倍。所以分组就是为了缓解消费端的压力的,为了横向扩展,数据量过大的话,如果多线程解决不了,就加机器嘛, 分组就是给他们一个唯一标识,组内可以进行负载均衡。Leader的作用就是进行消费者和分区之间的负载均衡

 

我们再来看一张broker内部是怎么组成的

再来思考几个问题:

1.Broker中存储这全量的消息,cunsumer怎么知道我上次消费到哪里了呢?消费者的偏移量Offset存储在哪里?

在broker中有一个topic专门存储_consumer_offset?  我的第一想法是应该存储在consumer中,自己携带着offset去消费就好了。但是这样会有一个问题,如果消费者进行重平衡了,根本不知道上次消费到哪里了。所以在broker中也存储一份。 

2 .Broker中宕机了,如果该机器存在partitionleader,数据丢失怎么办?

这就是开篇kafka定义中的复制的意义所在了,即使是leader宕机了,还有follower同步leader所有的消息。会选举一个follower作为leader。

 

深入kafka

Kafka如何进行复制

HW High Watermark俗称高水位,它标识了一个特定的消息偏移量(offset),消费者只能拉取到这个offset之前的消息。

LEO Log End Offset),标识当前日志文件中下一条待写入的消息的offset

1. 为什么消费者只能拉取到这个offset之前的消息?

大家思考一下这样的场景,如果消费者可以消费HW之后的消息,但是这个消息还没有写入follow,这时候leader宕机了,follower1成为了leader,而这个新leader没有45消息。Cunsumer去提交的时候,就没有这个offset呀,这样的消费端和broker的数据就会不一致。

2、如何确保新选举出的 leader 是优选呢?(网络原因可能不是所有follow都复制了leader上的数据)
Kafka 机制中,leader 将负责维护和跟踪一个 ISRIn-Sync Replicas列表,即同步副本队列,这个列表里面的副本与 leader 保持同步,状态一致。 KafkaZookeeper中动态维护了一个ISRin-sync replicasset这个set里的所有replica都跟上了leader(定时去zookeeper中更新)

Kafka得存储细节

Broker下有多个topic, 一个topic下有多个partition,一个partition下有多个segment

1、如何从 partition 中通过 offset 查找 message 呢?

比如查找170418。先找index 在通过index找到log (从宏观上看到partiton的最小单位,但是实际存储message的地方是log,)

 

生产者producer

生产者 发送消息流程

注意这里的第六个步骤 这个ack的可以在配置问件中配置参数的(01all

1. 在发送消息时, ProducerRecord 包含了消息的 topic,partition , 等属性,消息首先会被序列化
2. 继而根据前面负载均衡所提到的,分区器的负载均衡机制会最终决定消息存放到哪个 broker 上面去。当消息确定了要放在哪台 Broker 上的分区之后 , 他们会被放在一个个的批次里 ( 批次可以理解为缓冲区 ) ,然后当批次中的消息数达到了 linger.ms 的值指定的 Broker 进行投递。
3. 如果投递成功,则会返回 RecordMetadata 对象,如果投递失败可根据是否重试以及重试次数继续投递重试,
4. 如果重试一定次数 ( 参数配置 ) 之后还是失败就返回异常表示发送消息失败。
 
 

消费者consumer

问题:什么时候会再均衡?群组协调器(broker)是怎么感知的消费者宕机? 

消费者死亡或者有新的消费者加入。  通过心跳感知。将“REBALANCE_IN_PROGRESS封装进心跳请求的响应中,发还给消费者实例。

重平衡分为两个步骤 1.加入组的JoinGroup请求,就是上面这个图。2 分配分区的SyncGroup请求。就下面这张图。

看图,首先要注意的是这个协调者是一个broker,其次仔细看,真正去给consumer分配分区,进行负载均衡的是成员2,也就是消费者组中的领导者。

 

重平衡的触发条件:1、组成员数量发生变化。2、订阅主题数量发生变化。3、订阅主题的分区数发生变化。

 

Kafka两种默认的分配策略.

Range 范围分区

假如现在有 10 个分区,3 个消费者,排序后的分区将会是0,1,2,3,4,5,6,7,8,9;消费者排序完之后将会是C1-0,C2-0,C3-0通过 partitions/consumer数 来决定每个消费者应该消费几个分区。如果除不尽,那么前面几个消费者将会多消费 1 个分区。

       例如,10/3 = 3 1 ,除不尽,那么 消费者 C1-0 便会多消费 1 个分区,最终分区分配结果如下:

 Range 范围分区的弊端:

       如上,只是针对 1 topic 而言,C1-0消费者多消费1个分区影响不是很大。如果有 N 多个 topic那么针对每个 topic消费者 C1-0 都将多消费 1 个分区,topic越多,C1-0 消费的分区会比其他消费者明显多消费 N 个分区。这就是 Range 范围分区的一个很明显的弊端了

 

RoudRobin轮询分区

RoundRobin 轮询分区策略,是把所有的 partition 和所有的 consumer 都列出来,然后按照 hascode 进行排序,最后通过轮询算法来分配 partition 给到各个消费者。

        轮询分区分为如下两种情况:①同一消费组内所有消费者订阅的消息都是相同的    ②同一消费者组内的消费者所订阅的消息不相同 

①如果同一消费组内,所有的消费者订阅的消息都是相同的,那么 RoundRobin 策略的分区分配会是均匀的。

        例如:同一消费者组中,有 个消费者C0C1C2都订阅了 2 个主题 t0  t1并且每个主题都有 3 个分区(p0p1p2)那么所订阅的所以分区可以标识为t0p0t0p1t0p2t1p0t1p1t1p2最终分区分配结果如下:

如果想要使用RoundRobin 轮询分区策略,必须满足如下两个条件:

      ①每个消费者订阅的主题,必须是相同的。因为如果不同消费者订阅的消息不相同,就会出现分区不均匀

 

  ②如果同一消费者组内,所订阅的消息是不相同的,那么在执行分区分配的时候,就不是完全的轮询分配,有可能会导致分区分配的不均匀。如果某个消费者没有订阅消费组内的某个 topic那么在分配分区的时候,此消费者将不会分配到这个 topic 的任何分区。

        例如:同一消费者组中,有3个消费者C0C1C2。有t0t1t2三个主题。C0订阅了t0t1C1订阅了t0t2C2订阅了t1,t2t0有三个分区t0p0t0p1t0p2,t1有两个分区 t1p0t1p1T2有两个分区t2p0,t2p1。最终分区分配结果如下:

 

提交和偏移量

      消费者提交偏移量的主要是消费者往一个名为_consumer_offset的特殊主题发送消息,这个主题会保存每次所发送消息中的分区偏移量,这个主题的主要作用就是消费者触发重平衡后记录偏移使用的,消费者每次向这个主题发送消息,正常情况下不触发重平衡,这个主题是不起作用的,当触发重平衡后,消费者停止工作,每个消费者可能会分到对应的分区,这个主题就是让消费者能够继续处理消息所设置的。

 

1. 自动提交(会出现重复消费消息的情况 5s 提交一次 3s 的时候再均衡 )(消费者自提交到 11 ,但是没处理完 6-11 的数据,消费者宕机了, 6-11 的数据有一部分没处理完,下次消费从 11 之后,这段消息就丢失了)
2. 提交当前偏移量
3. 异步提交(如果上一次提交的位置在网络中阻塞了,就也会出现重复消费消息的情况)
 

消费者配置

1. bootstrap.servers
2. Group.id
3. Kety.deserializer
4. Value.deserializer
5. Fetch.min.bytes (消费者从 broker 获取消息的最小字节数,如果 broker 中不够,就会等到消息大小达到这个数值的时候发送给消费者(基于 TCP 协议 长链接))
6. Fetch.max.wait.ms (默认 500ms
7. Max.partition.fetch.bytes broker 从每个分区返回给消费者的最大字节数 1M ,举个栗子:一个主题有 20 个分区 5 个消费者。那么一个消费者至少要 4M 内存来接收)
8. Session.timeout.ms (指定了消费者可以多久不发送心跳)
9. Heartbeat.interval.ms poll 方法向协调器发送心跳的频率)
10. Auto.offset.reset latest 在偏移量无效的情况下从最新的 · 位置读取、 earliest
11. Enable.auto.commit (默认是 true 是否自动提交)
12. auto.commit.interval.ms (自动提交的频率)
13. Partition.assignment.strategy (决定消费者分配分区的时候的分配策略 后面讲一下分配策略)
14. Client.id
15. Max.poll.records (可以控制调用 call 方法能够返回的记录数量)
16. Receive.buffer.bytes send.buffer.bytes TCP 缓冲区大小)
 
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值