kafka相关概念
重要模块和参数
Broker:一个kafka节点就是一个broker,多个broker可以组成一个kafka集群。
Topic:消息主题,属于逻辑概念,根据topic对消息进行分类。
Partition:分区,属于物理概念,存储消息的载体,一个topic可以设置多个partition,在每个partition内部消息是有序的。
ConsumerGroup: 消费者组,每个Consumer属于一个规定的Consumer Group。
Buffer_memory:消息缓冲区,默认值32MB,如果设置了该值,消息会先发送到本地缓冲区;再定时批量将消息发送到broker,用来提升发送消息的性能(默认一个批次发送16kb的数据)。
replicas:表示某个partition在哪几个broker上存在备份。就算这个节点挂了,也会列出。如图,partition0的replicas是0,1,2(实际顺序可能会有差异)。
isr:当前存活的节点,并且已同步备份了该partition的节点,与leader同步滞后的节点会被踢出该列表。如图,Partition0的isr是0,1,2;若broker2故障挂掉了,则isr会变为0,1。
特点
1、一个topic可以设置多个partition,同一个partition可以配置多个副本存在不同的broker(kafka节点)上。
2、同一个partition,会有Leader节点和Follower节点,Leader节点来提供读写数据,Follower同步Leader的数据(为了保证多副本数据与消费的一致性),消息也是存储在partition中的。
3、消息在partition中是有序的,要想实现顺序消息,只需指定partition来发送消息
4、一条消息可以被多个不同的Consumer Group消费,但是一个Consumer Group中只能有一个Consumer能够消费该消息
5、要实现广播模式,只需要将Consumer定义在不同的组即可。
kafka高性能原因
1、磁盘顺序读写:kafka消息不能修改,更不会从文件中间删除保证了磁盘顺序读,kafka的消息写入文件都是追加在文件末尾,不会写入文件中的某个位置(随机写)保证了磁盘顺序写。
2、数据传输的零拷贝:通过操作系统的sendfile来实现
3、消息缓冲区,以及写数据的批量batch处理和压缩传输
4、比RocketMQ的吞吐量要高
Kafka的生产者采用的是异步发送消息机制,当发送一条消息时,消息并没有发送到Broker而是缓存起来,然后直接向业务返回成功,当缓存的消息达到一定数量时再批量发送给Broker。这种做法减少了网络Io,从而提高了消息发送的吞吐量,但是如果消息生产者宕机,会导致消息丢失,业务出错,所以理论上kafka利用此机制提升了性能却降低了可靠性。
零拷贝参考:
存储文件
commit log消息存储文件
一个partition分区的消息数据对应存储在一个文件夹下,以topic名称+分区号命名,消息在分区内是分段存储,每个段的消息都存储在不一样的commit log文件里,每个commit log里的消息都有一个唯一的标识offset偏移量,且消息是以顺序写的形式依次写入文件。
kafka规定了一个log文件最大为1G,做这个限制目的是为了方便把 log 文件加载到内存去操作:
如上图,TopicA的partition0的分区文件目录为:/TopicA-0,该目录下主要有两个日志文件xxxx.log文件(主要存offset和消息体 )和xxxx.index文件(主要存index)。这两个文件都是以开始的offset来命名的。
如下文件
00000000000000000000.index, 00000000000000000000.log 表示刚开始写入的第一个文件
00000000000006666651.index, 00000000000006666651.log 表示从offset为6666651开始写入的文件,可知前面已经写入了600多万消息了。
__consumer_offsets文件
记录每个consumer消费到分区里的最新的offset,默认有50个这样的文件。
每个consumer是基于自己在commit log中的消费进度(offset)来进行工作的。在kafka中,消费offset由consumer自己来维护;一般情况下我们按照顺序逐条消费commit log中的消息。
每个consumer会定期提交自己消费的offset到__consumer_offsets;提交的数据里,其中key是consumerGroupId+topic+分区号,value就是当前offset的值;kafka会根据offset定期清理分区中已经消费的消息,保留最新的那条数据。
通过以下公式来计算得到consumer消费的offset要提交到哪个分区
消费者组id的hash值与__consumer_offsets数量取模
hash(consumerGroupId) % __consumer_offsets主题的分区数
消息确认机制
生产者发送消息acks
默认设置为all
1、acks=0: 表示producer不需要等待任何broker确认收到消息的回复,就可以继续发送下一条消息。性能最高,但是最容易丢消息。
2、acks=1: 至少要等待leader已经成功将数据写入本地log,但是不需要等待所有follower是否成功写入。就可以继续发送下一条消息。这种情况下,如果follower没有成功备份数据,而此时leader又挂掉,则消息会丢失。
3、acks=-1或all:这种策略会保证只要有一个备份存活就不会丢失数据。这是最强的数据保证。
消费者消费消息ack
MANUAL:调用ack.acknowledge手动确认ack
RECORD:每一条记录被消费者监听器(ListenerConsumer)处理之后提交
BATCH:当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后提交
消息写入流程
1、producer根据规则选出分区patition
指定了patition则直接使用;未指定patition但指定 key,通过对 key 的 value 进行hash选出一个 patition;patition 和 key 都未指定,使用轮询选出一个 patition
2、producer从zookeeper的 “/brokers/…/state” 节点找到该partition的leader。
3、producer 将消息发送给该leader
4、leader 将消息写入本地 log
5、followers 从 leader 同步消息,写入本地 log后向leader发送 ACK
6、leader 收到所有 isr列表节点 的ACK后,增加 HW(最后 commit 的 offset) 并向 producer 发送 ACK,消息写入完毕。
消息删除机制
kafka一般不会删除消息,不管消息有没有被消费。只会根据配置的日志保留时间(log.retention.hours)确认消息多久被删除,默认保留最近一周的日志消息。
核心总控制器Controller
在集群启动的时候,会选举一台broker作为controller来管理整个集群,每个broker都会尝试在zookeeper上创建一个 /controller 临时节点,谁创建成功谁成为controller。其他broker监听该临时节点,若controller宕机,则临时节点会自动失效删除,则重新尝试竞争。
controller的职责:
1、监听broker相关的变化/brokers/ids/
2、监听topic相关的变化/brokers/topics/
3、更新集群的元数据信息,同步到其他普通的broker节点中
Partition选举Leader机制
刚开始启动服务时,以第一个broker为leader;如果分区leader所在的broker挂了,controller会从isr列表选择第一个broker作为leader,因为第一个broker最先放进ISR列表,可能是同步数据最多的副本。
消费者Rebalance机制
触发条件
1、消费组里的consumer增加或减少了
2、动态给topic增加了新的分区
3、消费组订阅了更多的topic
分区分配策略
1、range策略:就是按照分区序号排序。如0-1给一个消费者,2-3给一个消费者,4-5给一个消费者
2、round-robin策略:就是轮询分配。如0-3给一个消费者,1-4给一个消费者,2-5给一个消费者
3、sticky策略:初始时分配策略与round-robin类似,但是要保证:分区的分配要尽可能均匀 ;分区的分配尽可能与上次分配的保持相同。
Rebalance过程
1、consumer发送FindCoordinatorRequest请求来查找对应的组协调器GroupCoordinator。
组协调器选择方式:
consumer消费的offset要提交到__consumer_offsets的哪个分区,这个分区leader对应的broker就是这个consumer group的coordinator
2、consumer会向组协调器GroupCoordinator 发送 JoinGroupRequest 请求。
3、组协调器GroupCoordinator选择第一个加入group的consumer作为leader,接着这个leader会负责制定分区方案。
4、consumer leader发送SyncGroupRequest请求将分区方案发送给GroupCoordinator。
5、GroupCoordinator就把分区方案下发给各个consumer。
Kafka的Pull和Push区别
1、pull表示消费者主动拉取,可以批量拉取,也可以单条拉取,所以pull可以由消费者自己控制,根据自己的消息处理能力来进行控制,但是消费者不能及时知道是否有消息,可能会拉到的消息为空。
2、push表示Broker主动给消费者推送消息,所以肯定是有消息时才会推送,但是消费者不能按自己的能力来消费消息,推过来多少消息,消费者就得消费多少消息,所以可能会造成网络堵塞,消费者压力大等问题。