kafka学习笔记

一、什么是kafka

kafka是一个消息队列,更贴切的说是一个消息引擎系统;一般来说,消息传递的方式有点到点模型和发布/订阅模型;kafka支持这两种模型,但更多时候我们讨论的是它的发布/订阅模型;通过kafka,可以有效的隔离上下游系统,使得上下游可以实现解耦;之所以要使用kafka隔离上下游,就是为了“削峰填谷”;如果上游突增的访问量直接来到下游,可能会造成下游系统的奔溃,而通过kafka进行缓冲,可以有效解决这个问题, kafka可以应对上游突增的访问量,并平滑的把消息传递给下游。

kafka是一个高吞吐量的消息引擎系统,吞吐量指的是单位时间内能处理的请求数量。kafka可以满足每秒百万级别的生产和消费。为什么kafka处理速度可以这么快?并不是因为它把数据都存放在内存,相反,kafka的数据都存放在磁盘,kafka的消息存放在磁盘的一个个日志文件,并且只能追加写操作,这样可以保证kafka的数据在磁盘中都是有序的。kafka正是利用了磁盘顺序读写速度超过内存随机读写速度的机制实现高吞吐量。

二、kafka常用命令

注:kafka默认端口号9092,zookeeper默认端口号2181;由于kafka依赖于zookeeper,这里假设zookeeper和kafka都安装在localhost上;hello代表主题名,con-1代表消费者组名)

  1. 启动停止命令
启动kafka:bin/kafka-server-start.sh -daemon config/server.properties
停止kafka:bin/kafka-server-stop.sh
  1. 主题相关命令
创建topic:bin/kafka-topics.sh --create --zookeeper localhost:2181 --partitions 2 --replication-factor 1 --topic hello
查看kafka所有topic列表:bin/kafka-topics.sh --list --zookeeper localhost:2181
查看某个topic详细信息:bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic hello
修改topic(一般是修改partition数量,只能增加):bin/kafka-topics.sh --alter --zookeeper localhost:2181 --partitions 5 --topic hello
删除topic(删除操作不可逆,没有回收站功能):bin/kafka-topics.sh --delete --zookeeper localhost:2181 --topic hello
  1. 生产者消费者相关命令
生产者生产数据:bin/kafka-console-producer.sh --broker-list localhost:9092 --topic hello
消费者消费数据(消费topic最新数据):bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic hello
消费者消费数据(消费topic所有数据):bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic hello --from-beginning
  1. 消费者组相关命令
查看所有消费者组:bin/kafka-consumer-groups.sh --list --bootstrap-server localhost:9092
查看某个消费者组的详细信息:bin/kafka-consumer-groups.sh --describe --bootstrap-server localhost:9092 --group con-1
三、kafka为何要对topic进行分区

分区是实现负载均衡以及高吞吐量的关键;不同的分区能够被放置到不同节点的机器上,而数据的读写操作也都是针对分区这个粒度而进行的,这样每个节点的机器都能独立地执行各自分区的读写请求处理。并且,我们还可以通过添加新的节点机器来增加整体系统的吞吐量。

在分布式系统中,通常会选举出一个leader节点作为集群的主节点,在分布式选举算法中,有一个很重要也很常见的共识算法:Raft算法,在这个算法中,集群中的节点有三种角色:leader、candidate、follower;强领导者模型中的leader节点负责集群的写操作,也就是说,客户端发起的所有写操作都会由leader节点负责,这样一来是会限制集群的写性能的;再举一个例子,mysql的主从架构中,主节点负责写操作,从节点负责读操作,由于集群中的主节点只有一个,写操作就会导致系统出现性能瓶颈。

kafka的分区很好的解决了这个问题,是很多分布式系统可以借鉴的一种解决集群写性能瓶颈的方案。kafka一个topic会对应多个partition,每个partition可以位于不同的broker上,并且每个partition会对应多个副本,对于每个partition,也相当于构成了一个集群,多个副本位于多个broker上,会选举出一个leader来进行该partition的写操作,这样一来,当生产者往topic的多个partition写数据时,就可能是多个broker在处理写操作,不仅可以提高kafka写数据的速度,提高吞吐量,还不会导致写性能瓶颈。

举个通俗易懂的例子:有一个三节点的kafka集群,三个节点分别是brokerA、brokerB、brokerC,有一个topic,对应两个partition,分别是part0和part1,每个partition的副本数是3,假设part0和part1的三个副本分别位于三个节点上,也就是brokerA、brokerB、brokerC都有part0和part1的副本,对于part0来说,它的leader位于brokerA,对于part1来说,它的leader位于brokerB,当生产者往topic写入数据时,会分别往part0和part1写入数据,是由brokerA上的part0 leader和brokerB上的part1 leader进行处理的,相当于生产者写入数据时,是由kafka集群的多个节点同时处理的,这比起其它分布式系统中只有一个节点处理写操作要高效得多。

四、kafka的分区策略

所谓分区策略是决定生产者将消息发送到哪个分区的算法

  1. 轮询策略:轮询策略是Kafka Java生产者API默认提供的分区策略;也称Round-robin策略,即顺序分配。它总是能保证消息最大限度地被平均分配到所有分区上;比如一个主题下有3个分区,往这个主题打入多条消息时,那么第一条消息被发送到分区0,第二条被发送到分区1,第三条被发送到分区2,以此类推。当生产第4条消息时又会重新开始,即将其分配到分区0。
  2. 按消息键保序策略:可以给每个消息定义一个key,一旦消息被定义了key,那么就可以保证同一个key的所有消息都进入到相同的分区里面,由于每个分区下的消息处理都是有顺序的,故这个策略被称为按消息键保序策略。

kafka默认分区策略实际上同时实现了两种策略:如果消息指定了key,那么默认实现按消息键保序策略;如果没有指定key,则使用轮询策略。

五、kafka的消费者组
  1. 发布订阅模式要求每个订阅者都必须要订阅主题的所有分区,这种全量订阅的方式既不灵活,也会影响消息的真实投递效果;kafka的消费者组可以解决这一个问题,当消费者组订阅了多个主题后,组内的每个消费者实例不要求一定要订阅主题的所有分区,它只会消费部分分区中的消息。
  2. 同一个消费者组的消费者不能消费同一分区数据,不同消费者组的消费者可以消费同一分区数据。
  3. 消费者组这一种机制,可以同时实现了传统消息引擎系统的两大模型: 如果所有消费者实例都属于同一个消费者组,那么它实现的就是消息队列模型(点到点模型)。如果所有消费者实例分别属于不同的消费者组,那么它实现的就是发布/订阅模型。
  4. 理想情况下,一个消费者组的消费者实例数量应该等于该消费者组订阅主题的分区总数。
  5. 关于消费者组的Rebalance(重平衡):
    a. Rebalance本质上是一种协议,规定同一个消费者组里的消费者实例合理分配订阅Topic的每个分区。
    b. 当组成员数发生变更、订阅主题数发生变更、订阅主题的分区数发生变更都会触发消费者组的Rebalance过程。
    c. Rebalance过程和JVM的垃圾回收stop the world类似,在Rebalance过程中,所有Consumer实例都会停止消费,等待Rebalance完成。
六、kafka如何保证数据不丢

kafka只对“已提交”的消息做有限度的持久化保证,至于什么是“已提交”,由开发者自己决定,通过acks机制来设置的

  • acks默认为1,表示只需要leader角色的分区副本回复收到消息,生产者就会发送下一条数据
  • acks的值为all,表示需要所有分区副本(leader+follower)回复收到消息,这样生产者才会发送下一条数据,如果设置acks为all,则可以保证数据不丢
  • acks的值为0,表示不需要任何分区副本回复,生产者会继续发送下一条数据;丢失数据的可能性较高

生产者往leader角色的分区副本写入数据后,数据会从leader复制到follower,那么对于生产者来说,怎么样才算数据写入成功?对于leader复制数据到follower,分同步复制和异步复制两种情况,我们可以通过决定producer是否等待消息被提交的通知(ack)来区分同步复制和异步复制。
如果等待ack则为同步,如果不需要等待所有follower复制完成即回传ack则为异步模式。

同步复制:

1.producer联系zk识别leader
2.向leader发送消息
3.leadr收到消息写入到本地log
4.follower从leader pull消息
5.follower向本地写入log
6.follower向leader发送ack消息
7.leader收到所有follower的ack消息
8.leader向producer回传ack

异步复制:和同步复制的区别在于,leader写入本地log之后,直接向producer回传ack消息,不需要等待所有follower复制完成。

七、消费者的消费位移管理
  1. 老版本消费者的消费位移管理是依托于Apache ZooKeeper的,但是ZooKeeper并不适用于这种高频的写操作。
  2. 新版本消费者的消费位移管理是保存在Broker的一个内部主题_consumer_offsets中的。
  3. 消费者的消费位移,记录的是消费者要消费的下一条消息的位移,而不是目前最新消费消息的位移。
八、kafka的消费位移提交
  1. 从用户的角度来说,消费位移提交分为自动提交和手动提交;对于手动提交而言,又分为同步提交和异步提交。
  2. 开启自动提交只需要将参数enable.auto.commit设置为true即可,kafka默认就是开启自动提交,无需手动设置。对于自动提交,一般还有个参数需要配置:auto.commit.interval.ms;它的默认值是5秒,表明kafka每5秒会为你自动提交一次位移。
  3. 对于同步提交:consumer端调用commitSync()方法之后,会一直等待,直到消费位移被成功提交才会返回。如果提交过程中出现异常,该方法会将异常信息抛出。它有一个缺陷,就是在调用commitSync()方法时,consumer程序会处于阻塞状态,直到远端的Broker返回提交结果,这个状态才会结束。
  4. 对于异步提交:consumer端调用commitAsync()方法之后,它会立即返回,不会阻塞;它的缺陷在于出现问题时它不会自动重试。
九、kafka消费数据的流程

先根据group.id指定的消费者组到kafka中查找之前保存的offset信息,如果查找到了,说明之前使用这个消费者组消费过数据,则根据之前保存的offset继续进行消费;如果没查找到(说明第一次消费),或者查找到了,但是查找到的那个offset对应的数据已经不存在了(因为kafka默认只会保存7天的数据,超过时间数据会被删除),此时会根据auto.offset.reset的值执行不同的消费逻辑,这个参数的值有三种:[earliest,latest,none]。earliest:表示从最早的数据开始消费(从头消费),latest【默认】:表示从最新的数据开始消费,none:如果根据指定的group.id没有找到之前消费的offset信息,就会抛异常。
注意:auto.offset.reset这个参数只有在消费者第一次消费数据,或者之前保存的offset信息已过期的情况下才会生效

十、Consumer消费顺序
  1. 当一个消费者消费一个partition时候,消费的数据顺序和此partition数据的生产顺序是一致的;
  2. 当一个消费者消费多个partition时候,消费者按照partition的顺序,首先消费一个partition,当消费完一个partition最新的数据后再消费其它partition中的数据;
  3. 如果一个消费者消费多个partiton,只能保证消费的数据顺序在一个partition内是有序的,也就是说消费kafka中的数据只能保证消费partition内的数据是有序的,多个partition之间是无序的;
  4. 如果对数据的顺序有严格要求,希望消费数据的顺序和生产数据的顺序一模一样,那么只能设置该topic为单分区。
十一、kafka关于消费数据的三种语义
  1. 至少一次(at-least-once):这种语义可以保证消费时不丢失数据,但有可能会对数据重复处理。实现方式:设为手动提交offset;有可能处理多次的场景是消费者的消息处理完并输出到结果库,但是offset还没提交,这个时候消费者挂掉了,再重启的时候会重新消费并处理消息,所以至少会处理一次。
  2. 至多一次(at-most-once):这种语义不会对数据重复处理,但有可能会丢失数据。实现方式:设为自动提交offset;有可能丢失数据的场景是消费者的offset已经提交,但是消息还在处理中(还没有处理完),这个时候程序挂了,导致数据没有被成功处理,再重启的时候会从上次提交的offset处消费,导致上次没有被成功处理的消息就丢失了。
  3. 仅一次(exactly-once):这种语义可以保证数据只被消费处理一次,既不会丢失数据也不会重复消费数据。
十二、kafka如何通过offset快速定位

问题:kafka每次消费都会记录一个offset,即消费位移,下次消费时,可以通过这个offset继续往下消费,那么kafka如何通过这个offset快速的定位到本次要消费的位置?

主要是由于kafka数据的存储方式使用了“分段+索引”;

kafak中数据的存储方式是这样的: 每个partition由多个segment【片段】组成,每个segment中存储多条消息;每个partition在内存中对应一个index,记录每个segment中的第一条消息偏移量。
在这里插入图片描述
kafka中数据的存储流程是这样的:topic收到消息后往对应partition的最后一个segment上添加该消息,segment达到一定的大小后会创建新的segment。由于消息的偏移量都是递增的,这样后期查找起来就方便了,先到索引中判断数据在哪个segment文件中,然后就可以直接定位到具体的segment文件了,这样再找具体的那一条数据就很快了,因为都是有序的。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值