几张图理解kafka的设计原理(一)

778 阅读5分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

kafka是一个分布式流平台,高吞吐量的消息系统。非常经典的是用于削峰、解耦、异步。 更多解释请看 【简约入门】消息队列,以kafka开始

本文针对kafka的Leader选举、consumer group、offset这些术语和它们的作用简单的用图来解释一下。

Leader选举

  • Leader选举过程 Kakfa Broker Leader的选举:Kakfa Broker集群受zookeeper管理。所有的Kafka Broker节点一起去zookeeper上注册一个临时节点,因为只有一个Kafka Broker会注册成功,其他的都会失败,所以这个成功在zookeeper上注册临时节点的这个Kafka Broker会成为Kafka Broker Controller,其他的Kafka brokerKafka Broker follower。(这个过程叫ControllerZooKeeper注册Watch)。

break1.png 这个controller会监听其他的Kafka Broker的所有信息,如果这个kafka broker controller宕机了,在zookeeper上面的那个临时节点就会消失。

leader.png

  • broker宕机,副本成为leader 例如:一旦有一个broker宕机了,这个kafka broker controller会读取该宕机broker上所有的partitionzookeeper上的状态,并选取ISR列表中的一个replica作为partition leader(如果ISR列表中的replica全挂,选一个幸存的replica作为leader;

如果该partition的所有的replica都宕机了,则将新的leader设置为-1,等待恢复,等待ISR中的任一个Replica“活”过来,并且选它作为Leader;或选择第一个“活”过来的replica(不一定是ISR中的)作为Leader),这个broker宕机的事情,kafka controller也会通知zookeeperzookeeper就会通知其他的kafka broker

isr.png

Consumer group

  • consumer消费规则 kafka中的group由多个consumer组成,topic下面的partition中的每条消息只能被consumer group中的一个consumer消费,如果想多个consumer消费这条消息,那么这些consumer必须在不同的组。Kafka不支持一个partition中的消息由两个或两个以上的同一个consumer group下的consumer thread来处理,除非再启动一个新的consumer group,代码中常常不同服务设置不同的组名。所以如果想同时对一个topic做消费的话,启动多个consumer group就可以了。

但是要注意的是,这里的多个consumer的消费都必须是顺序读取partition里面的消息,新启动的consumer默认从partition队列最头端的地方开始读消息。kafka为了保证吞吐量,只允许同一个consumer group下的一个consumer线程去访问一个partition

如果觉得效率不高的时候,可以加partition的数量来横向扩展,那么再加新的consumer thread去消费。如果想多个不同的业务都需要这个topic的数据,起多个consumer group就好了,大家都是顺序的读取消息,offset的值互不影响。这样没有锁竞争,充分发挥了横向的扩展性,吞吐量极高。这也就形成了分布式消费的概念。

topic1.png

  • 最优的消费配置 当启动一个consumer group去消费一个topic的时候,无论topic里面有多个少个partition,无论我们consumer group里面配置了多少个consumer thread,这个consumer group下面的所有consumer thread一定会消费全部的partition;即便这个consumer group下只有一个consumer thread,那么这个consumer thread也会去消费所有的partition。因此,最优的设计就是,consumer group下的consumer thread的数量等于partition数量,这样效率是最高的。

一个consumer group下,无论有多少个consumer,这个consumer group一定回去把这个topic下所有的partition都消费了。当consumer group里面的consumer数量小于这个topic下的partition数量的时候,就会出现一个conusmer thread消费多个partition的情况,总之是这个topic下的partition都会被消费。

如果consumer group里面的consumer数量等于这个topic下的partition数量的时候,此时效率是最高的,每个partition都有一个consumer thread去消费。当consumer group里面的consumer数量大于这个topic下的partition数量的时候,就会有一个consumer thread空闲。因此,我们在设定consumer group的时候,只需要指明里面有几个consumer数量即可,无需指定对应的消费partition序号,consumer会自动进行rebalance

part.png

offset更新策略

  • offset的更新 consumer消费partition里面的消息的时候是O(1)顺序读取的。所以必须维护着上一次读到哪里的offset信息。offset一般存于zookeeper中,low level APIoffset由自己维护。consumer默认是读完消息先commmit再处理这条消息,autocommit默认是true,这时候先commit就会更新offset+1,一旦处理失败,offset已经+1,这个时候就会丢消息;也可以配置成读完消息处理再commit,这种情况下consumer端的响应就会比较慢的,需要等处理完才行。
  • 消费提交offset过程 Kafka producer发送消息不用维护消息的offset信息,因为这个时候,offset就相当于一个自增idproducer就尽管发送消息就好了。而且KafkaActiveMQ不同,ActiveMQ大都用在处理业务逻辑上,而Kafka大都是处理日志,所以Kafkaproducer一般都是大批量的batch发送消息,向这个topic一次性发送一大批消息,load balance到一个partition上,一起插进去,offset作为自增id自己增加就好。

但是consumer端是需要维护这个partition当前消费到哪个offset信息的,并且kakfa处理消息是没有锁操作的。因此如果处理消息失败,此时还没有commit offset+1,当consumer thread重启后会重复消费这条消息。但是作为高吞吐量高并发的实时处理系统,at least once的情况下,至少一次会被处理到,是可以容忍的。如果无法容忍,就得使用自己程序维护这个offset信息,那么想什么时候commit offset+1就自己控制了。

offset.png

参考