注:文章版本对应官网kafka version 2.3

一、文章目录

  • kafka负载均衡
  • kafka消费者组概念
  • kafka消息顺序
  • kafka为什么高效
  • kafka分区下的文件格式及内容
  • kafka如何根据偏移来定位消息

二、具体内容

1、负载均衡
  对于kafka集群,日志通常都以多副本的方式存储,每个Topic设置多个分区,每个分区在多台机器上(对应配置的副本数)都有备份。每个分区对应一个leader,提供读写服务。其它servers都是followers,followers只对分区进行复制。每个server对其中某些分区表现为leader,而其他server对这些分区表现为followers。这样一来,持有log的server均对外提供读写服务,只是每个server承担的对外提供服务的分区不同而已,这样读写压力就均分流到全部server上,从而实现负载均衡。

2、消费者组
  对于kafka的发布订阅机制来说,consumer消费kafka消息,通过consumer所属的consumer group来区分不同的consumer。发布到kafka topic分区中的消息会向所有订阅该topic的consumer group进行广播。但每个consumer group内仅有1个consumer能够消费该消息。也就是说同一consumer group内的所有consumer会分别消费发布到topic的不同分区,保证所有分区的消息都被消费到。换句话讲,partition和同一consumer group内的consumer是一对一或者多对一的关系。
  同一个consumer group中有多个consumer是考虑弹性扩展和容错使用。当其中一个consumer挂掉之后,该consumer负责消费的分区数据会被发送到其他consumer进行消费。

3、顺序
  kafka只对同一分区内的数据保持顺序,并不对整个topic的数据保持顺序。
  在数据写入磁盘的时候,对topic的每个分区,维护一个原子性的计数器,用来记录log文件append消息Message的总偏移数,以便通过节点id+分区id+offset来定位message。

4、kafka为什么高效
(1)kafka顺序读写磁盘
(2)传统磁盘文件发送到socket的低效过程

  • 操作系统将数据从磁盘读取到内核空间的页缓存。
  • 应用程序把数据从内核空间的页缓存中拷贝到用户空间的缓存buffer中。
  • 应用程序把数据从用户空间的缓存buffer中拷贝到内核空间的socket buffer中。
  • 操作系统把数据从内核空间的socket buffer中拷贝到NIC buffer用于发送。

(3)kafka的零拷贝 sendfile系统调用

  • 操作系统将数据从磁盘读取到内核空间的页缓存。
  • 操作系统将数据从页缓存拷贝到NIC buffer用于发送。

  可以看到kafka的数据发送方式,减少了2次内核空间和用户空间之间的数据拷贝,并且这个过程在内核态进行,并没有2次上下文切换的开销。

5、kafka对应分区目录下的文件格式

注:测试配置(仅为显示内容使用)

log.segment.bytes=102400   每个segment达到102400时生成一个新的segment记录新消息
log.index.interval.bytes=4096  默认值,每隔4096字节,对消息设置一个索引

kafka负载不均 kafka负载均衡原理_零拷贝


test-0分区目录下的文件列表

其中.index和.log分别存储索引和数据
命名规则:

  • 文件名:通过取文件中第一条消息的起始偏移量offset值来命名,可以看到,由20个字符长度组成。

我们看看这两个文件的具体内容,先看.index文件,如下

rxtdeMBP:test-0 rttian$ ~/Documents/work/bigdata/kafka_2.12-2.3.0/bin/kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.index
Dumping 00000000000000000000.index
offset: 71 position: 4151
offset: 127 position: 14038
offset: 180 position: 20758
offset: 282 position: 27479
offset: 332 position: 34108
offset: 335 position: 40737
offset: 480 position: 57200
offset: 625 position: 77064

  可以看到,index文件中存储的是索引消息的offset,和其在对应log文件的物理字节偏移量position。需要注意的是虽然是用每隔4096字节的默认值设置一个index,可以看到第二行中的值offset: 127 position: 14038显然不符合这一规则,这是为什么呢?原因在于在给消息设置索引的时候,并不是按照一条一条的消息来计算其偏移,而是对每次flush到磁盘上的一批数据来进行,只要此次写入到磁盘的总数据超过4096,就为下一次写入的消息设置一个索引。

再看.log文件的内容

rxtdeMBP:test-0 rttian$ ~/Documents/work/bigdata/kafka_2.12-2.3.0/bin/kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.log --print-data-log
Dumping 00000000000000000000.log
Starting offset: 0
baseOffset: 0 lastOffset: 1 count: 2 baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: -1 partitionLeaderEpoch: 0 isTransactional: false isControl: false position: 0 CreateTime: 1567500758701 size: 162 magic: 2 compresscodec: NONE crc: 2221167045 isvalid: true
| offset: 0 CreateTime: 1567500758127 keysize: -1 valuesize: 43 sequence: -1 headerKeys: [] payload: This is a great way to learn the framework.
| offset: 1 CreateTime: 1567500758701 keysize: -1 valuesize: 43 sequence: -1 headerKeys: [] payload: This is a great way to learn the framework.
baseOffset: 2 lastOffset: 3 count: 2 baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: -1 partitionLeaderEpoch: 0 isTransactional: false isControl: false position: 162 CreateTime: 1567500760242 size: 162 magic: 2 compresscodec: NONE crc: 168766516 isvalid: true
| offset: 2 CreateTime: 1567500759463 keysize: -1 valuesize: 43 sequence: -1 headerKeys: [] payload: This is a great way to learn the framework.
| offset: 3 CreateTime: 1567500760242 keysize: -1 valuesize: 43 sequence: -1 headerKeys: [] payload: This is a great way to learn the framework.

6、kafka如何根据偏移定位消息
  读取时,应用程序提供一个全局偏移offset,kafka会根据这个偏移,利用二分查找的方式先找到消息的偏移在哪个.index文件中,然后在.index文件中拿出该offset在log文件中对应的物理位置(对应.index文件中的position),然后根据这个position就定位到对应的.log文件中该消息位置。