vlambda博客
学习文章列表

Kafka之基本概念与术语

kafka的定位

kafka最初的定位是分时消息引擎系统,后来又定位是分布式流式处理平台,这是因为随着kafka的演进,

交给下游数据处理平台做的事情也可以在kafka中做,因此在 0.10 版本中推出kafka streams流式处理组件,

因此kafka现在不仅仅是一个消息引擎系统,也是一个流式处理系统。

broker

不论是消息引擎还是流式处理,kafka的核心处理流程没有变过,总结如下:

  1. 生产者发送消息到kafka服务器

  2. 消费者从kafka服务器读取消息

  3. kafka服务器依托于Zookeeper进行服务的协调管理

kafka服务器被官方称为broker。

消息

kafka针对消息做出了许多优化,以此提升各方面的性能。

消息格式

kafka消息由多个字段组成,这些字段可分为两类,元数据与用户数据,元数据用于管理消息,用户数据则是用户控制发送的数据信息。

kafka消息格式有多个版本,这里介绍V1版本, 一条消息格式如下:

每个字段占用的大小如下:

  1. CRC:4B

  2. 版本号:1B

  3. 属性:1B,表示消息的压缩类型(0:无压缩,1:GZIP,2:Snappy,3:LZ4)

  4. 时间戳:8B

  5. key长度:4B,值表示key内容占用多少个字节

  6. key内容:k bytes

  7. value长度:4B,值表示value内容占用多少个字节

  8. value内容:v bytes

用户数据

普通用户只需要关心下面三个字段,其他都是元数据:

  1. key: 消息的key,用于对消息做partition使用,决定消息被保存在某topic下的某个partition中

  2. value: 消息实际的内容

  3. 时间戳: 消息发送时的时间戳, 可指定,如果不指定默认取当前时间

二进制保存的优化

kafka将消息使用二进制字节紧凑存储,这其中有一些特殊的优化考量,例如上面的字段如果使用java保存:

public class Message {
private CRC32 crc;
private short version;
private boolean codecEnabled;
private short codeClassOrdinal;
private String key;
private String value;
}

在Java内存模型(Java memory model 即 JMM)中,保存一个对象的开销通常比较大,一个小对象可能需要2倍的空间进行数据存储,随着堆中的数据越来越多,GC的性能就会下降,从而拖慢系统的吞吐量。

JMM虽然会对Java类进行字段重排减少对内存的占用,但是由于要求其对内存的占用必须是8的整数倍,如果没达到就需要用到padding内存填充,导致多出几个字节,

同时,一般运行Java的操作系统都默认开启页缓存机制,即可能对象在堆中保存一份又在页缓存中保存一份,这几种情况会造成资源浪费。

kafka在设计消息时,避开在Java堆上分配对象,直接使用ByteBuffer二进制数组进行堆外内存分配,kafka官网表示,在一台32GB的机器上,kafka可以使用到28-30G的物理内存,且不用担心GC性能,并且使用ByteBuffer保存同样的消息,会比Java堆中的对象减少40%的空间占用。

大量使用页缓存而非Java堆内存也可以使的Kafka broker进程崩溃时,虽然broker的堆内数据丢失了,但是页缓存的数据依然存在,下次重启时可以直接使用页缓存。

topic与partition

topic

topic意为主题,每个消息的发送都需要指定要发送到一个特定的topic中,同样对消息的订阅也需要指定topic。

不同的业务可以使用不同的topic,达到消息的隔离区分,例如业务A使用TopicA,业务B使用TopicB。

partition

topic一般会被多个消费者订阅,出于性能的考虑,又将topic分成了多个分区,即partition,每个topic由若干个partition组成,通过partition可分散负载压力。

partition是一个不可修改的有序消息序列,每个partition都有自己专属的编号,一般从0开始,用户对partition只能做出在消息序列尾部追加写入消息的操作,即顺序写入,

partition中的每条消息都会被分配一个唯一的序列号,该序列号被称为offset(位移),位移是从0开始顺序递增的整数,offset可以定位到partition中唯一的一条消息。

partition没有实际的业务含义,其引入是为了提升系统的吞吐量,创建topic时,可根据集群的规模设置具体的partition数量,实现吞吐量的提升。

消费者的offset

消费者端也有offset的概念,随着消费者对消息的读取,其在partition中对应的offset也会不断前移,但是不会超过服务端partition的offset。

在kafka中,可以通过 topic + partition + offset 的三元组合定位到唯一的一条消息。

replica(副本)

partition中保存着有序的消息日志,如果保存着partition的服务器宕机或者磁盘损坏等其他因素,导致该partition丢失,那么该部分的消息也跟着丢失,

因此实现可靠性就需要用到冗余机制,将partition日志备份多个副本(replica),防止数据丢失。

leader与follower

副本分为两种类型,领导者(leader)和追随者(follower),leader用于对外提供服务,follower保持与leader的状态同步,follower充当leader的候补,一旦leader挂掉,将从follower中选出一个新leader。

kafka保证同一个partition的多个replica不会分配在同一台broker上,而是分散在不同的broker上实现可靠性,即一台broker挂了可以利用另一台broker上的replica继续提供服务。

ISR

ISR的全称为 in-sync replica ,意为与leader replica保持同步的replica集合。

kafka为partition动态维护一个replica集合,集合中的replica状态斗鱼当前leader一致,只有该集合中的replica在leader宕机时可被选举为新的leader,

同时也只有该集合中的所有replica都保存了同一条消息,kafka才会将该消息标记为已提交状态,即认为该消息发送成功。

正常情况下partition中的所有replica都应该与leader replica保持同步,即所有replica都在ISR中,在运行过程中,如果有小部分的replica因为各种原因导致状态同步落后于leader replica,

在滞后到一定程度时,kafka会将这些replica踢出ISR集合,当这些replica重新追回同步进度时,kafka将他们重新加入ISR中。