将一组消息抽象归纳为一个主题(topic),一个主题是对消息的一个分类。生产者将消息发送到特定主题,消费者订阅主题或主题的某些分区进行消费
kafka通信的基本单位,由一个固定长度的消息头和一个可变长度的消息体构成。Kafka消息格式经历3此变迁,分别为V0,V1,V2,目前大部分用户使用的仍是V1,完整格式如下所示:
每个主题会被分成一个或多个分区(Partition)。每个分区由一系列有序、不可变的消息组成,是一个有序队列。
每个分区在物理上对应一个文件夹,分区的明明规则为:主题名称+’-’+分区编号(0,1,2,…)。
理论上,分区数越多吞吐量越高,分区是Kafka保证消息被顺序消费以及对消息进行负载均衡的基础。但Kafka只能保证一个分区之内消息的有序性,并不能保证跨分区消息的有序性。
Kafka的高吞吐率保障是每条消息被追加到相应的分区时,是顺序写磁盘。另外Kafka不会立即删除已被消费的消息,基于消息已存储的时间长度或分区的大小来清除老数据。
每个分区又有一到多个副本(Replica),分区的副本分布在集群的不同机器,以提高可用性。从存储角度看,分区的每个副本在逻辑上抽象为一个日志(Log)对象。Kafka保证同一个partition的多个replica一定不会分散在同一台broker上。
Kafka需要保证分区中不同副本的数据一致性,因而会选出一个Leader副本,而分区的其他副本即为Follower副本。只有Leader副本才负责处理客户端读/写请求,Follower副本从Leader副本同步数据。
任何发布到分区的消息会被直接追加到日志文件的尾部,而每条消息在日志文件中的位置都会对应一个按序递增的偏移量,他不表示消息在磁盘上的无力位置,只是一个分区下严格有序的逻辑值。
Kafka几乎不支持随机读写,因而没有提供额外索引机制。消费者可以通过控制消息偏移量来对消息进行消费,如消费者可以指定消费的起始偏移量,为了保证消息被顺序消费,消费者已消费的消息对应的偏移量也需要保存。
一个日志又被划分为多个日志段(LogSegement),日志段是Kafka日志对象分片的最小单位。一个日志段对应磁盘上的一个具体日志文件和两个索引文件,日志文件以".log"为后缀,用于保存消息实际数据;两个索引文件分别以".index"和’.timeindex"为文件名后缀,分别表示消息偏移量索引文件和消息时间戳索引文件
一个Kafka集群有一个或多个Kafka实例构成,每个Kafka实例可以称为broker,每个broker有一个唯一的非负整数标志id,也称为brokerId。
生产者负责将消息发送给代理,也即为向Kafka代理发送消息的客户端
消费者以拉去方式拉去数据,在Kafka中每一个消费者属于一个特定消费组,我们可以为每个消费者指定一个消费组,以groupId代表消费组名称。如果不指定消费组,则归属默认消费组test-consumer-group。
Kafka会为每个消费者生成一个全局唯一的id,格式为 g r o u p I d − {groupId}- groupId−{hostName}- t i m e s t a m p − {timestamp}- timestamp−{UUID前8位字符}。同一个主题的一条消息只能被同一个消费组下某一个消费者消费,但不同消费组的消费者可以同时消费该消息。消费组是Kafka用来实现对一个主题消息广播和单播的手段。
Kafka在Zookeeper中动态维护了一个ISR(In-sync Replica),即保存同步的副本列表,该列表中保存的是与Leader副本保持消息同步的所有副本对应的代理节点id。如果一个从副本当机宕机或落后太多,则该从副本将从ISR列表中移除,其中宕机包括代理被关闭,发生物理故障,心跳监测过期,网络延迟,进程关闭等。只有ISR列表中的副本才能被选举为Leader。只有所有在同一个partion中的replica都收到同一条消息后,该调消息才会被置为“已提交”状态,以允许客户端消费。
kafka利用ZooKeeper保存相应元数据信息。Kafka元数据信息包括如代理节点信息、Kafka集群信息、旧版消费者消息及其消费偏移量信息、主题信息、分区状态信息、分区副本分配方案信息、动态配置信息等。
Kafka在启动或运行过程会在ZooKeeper上创建相应节点来保存元数据信息,并通过Zookeeper监听机制在Zookeeper相应节点注册监听器来监听节点元数据的变化。通过Zookeeper可以很方便地对Kafka集群进行水平拓展和数据迁移
整体Kafka基本结构如下:
Kafka基于文件系统存储消息,Kafka线性写入消息数据,并结合现代操作系统的预读和延迟写等机制,确保完成消息数据高效读写,提供了常量时间的性能。同时保障了Kafka在及其重启后,已存储消息可继续恢复使用,以很好地支持在线、离线处理,以及和其他存储或流处理框架继承。
Kafka基于以下特性实现高吞吐量:
传统数据复制方法如下:
- 操作系统将数据从磁盘读取到内核空间页缓存
- 应用程序将数据从内核空间读入用户空间的缓冲区
- 应用程序将读到的数据写回内核空间并放入socket缓冲区
- 操作系统将数据从socket缓冲区复制到网卡接口,此时数据才能通过网络发送出去。
零拷贝的优化如下图所示:
Kafka基于ZooKeeper对集群进行协调管理,使Kafka更加容易进行水平拓展,生产者、消费者、代理都为分布式架构,同时集群拓展时,集群可以自动感知,重新进行负载均衡和数据复制。
Kafka基于会话管理实现故障转移。当主要服务器和备份服务器之间的心跳无法维持或祝服务器到服务衷心的会话超时过期时,集群会自动启动某个备份服务器来替代祝服务器
Kafka支持以下几种安全措施:
Kafka的代理是无状态的,即代理不记录消息是否被消息,消费偏移量的管理由消费者自己或组协调器来维护,同是集群本身几乎不需要生产者和消费者的状态信息。
Kafka支持Gzip、Snappy、LZ4这3种压缩方式,通常把多条消息放在一起组成MessageSet,然后再把MessageSet放到一个消息里,从而提高压缩比率来提高吞吐量
下面介绍几个配置参数,来了解他们是如何影响消息存储的可靠性
Kafka使用唯一的一个证书来标志每个broker,默认为-1,如果不制定,kafka会自动生成一个位移值。
制定kafka持久化消息的目录。如果待保存的消息非常多,最好确保文件夹下由充足的磁盘空间,可以通过逗号分割设置多个目录。这样Kafka可以把负载均匀地分配到多个目录,且可以将这些目录放在不同的物理磁盘,这样可以多个磁头同时执行写操作,极大地提升吞吐量。
是一个格式类似于ip1:port1,ip2:port2,ip3:port3
的字符串,制定了访问zookeeper的服务地址,在实际使用中,可以配置zk的子root,如ip1:port1/root1。来隔离zk其他访问者的配置。如果不配,默认为根路径。
格式类似于${协议1}://{主机名1}:${端口1},${协议2}://{主机名2}:${端口2}
,制定broker开放给客户端的监听地址。如配置主机名0.0.0.0来绑定所有网卡,允许所有ip来源访问。支持的协议类型包括PLAINTEXT、SSL及SASL_SSL。
每个分区的副本数,主题级别的配置参数是replication.factor,而broker级别可以通过配置default.replication.factor来配置自动创建的主题的默认分区副本数。
如果复制系数是3,则意味着每个分区会被3个不同的broker复制3份。
当分区首领不可用时,一个同步副本会被选为新首领。如果在选举过程没有数据丢失,即提交的数据同时存在于所有同步副本,则这个选举是完全的。
但在实际运行过程中,由于网络或其他原因,可能导致跟随者副本是不同步的,这时候首领宕机:
可以将unclean.leader.election.enable设为true,则为允许不同步的副本称为首领,会面临丢失消息的奉新啊。
指定消息数据的留存时间,如果同时设置,则优先选取ms,minutes次之,hours最后。默认的留存时间是7天。即Kafka会自动保存最新7天的数据,删除7天前的数据。kafka会根据消息的时间戳信息进行留存与否判断。对于没有时间戳的老版本消息格式,最用日志文件的最近修改时间判断。
指定消息数据的最大留存数据量,当分区日志大小超过这个参数时,即使未到达清理时间,kafka会自动清理该分区的过期日志段文件,参数默认值为-1,即永不根据消息日志文件总大小来删除日志
在多个分区副本里,可能会出现多个副本不可用的情况,可以配置min.insync.replicas,配置至少需要有多少个同步副本,才允许网分区内写入消息。这里看两种情况: