上篇文章介绍了kafka的设计概要,有点对点的队列模式,和消费生产的topic模式,kafka有着高吞吐,低延迟,伸缩性,消息持久化,负载均衡故障转移特性,kafka跟其他处理内存方式不同,内存高命中率来保证发送消息直接在内存操作,而持久化直接交给系统去处理,并且持久化采用的是顺序IO,sendFile零拷贝来保证高吞吐。Kafka的负载均衡则是采用broken和topic每个都有一个master和flower,每个topic的matser和flower不在同一个broken,这样保证一个服务器宕机,其他的flower也会存储数据,不会丢失,故障转移则是 会通过会话心跳的机制跟zookeeper来实现,通过服务注册入zookeeper中,一旦服务器停止,则会选举新的服务。伸缩性也是由zookeeper来配合的,因为有多个服务,这时候则需要考虑多个服务的一致性,服务的无状态 或者 轻量级状态可以保证效率更高,所以他们统一吧状态写入zookeeper保存。
Kafka到底是什么呢,是个分布式流处理平台,kafka刚开始确实是以消息引擎的身份出现的,其强大的传输效率 和 完备的分布式解决方案,随着kafka的不断演进,发现kafka下游数据处理平台做的事,自己也可以做,于是在kafka在0.10.0.0版本中,正式退出kafka streams,流处理框架。
其实不管是消息引擎还是流处理平台,生产者发消息给kafka服务,消费者从kafka服务消费消息,kafka服务依托与zookeeper集群进行协调管理。
1.1、kafka的消息
既然kafka核心功能是消息引擎,那么对消息的设计自然是首当其冲。
Kafka的消息格式是由多个字段组成,其中很多字段都是管理消息的元数据字段,对于用户来说是无法感知的,kafka消息一共经理三次变迁V0,V1,V2,目前大部分用户使用的应该是V1B版本,v1完成格式如下。
如图所示,消息由消息头部 和 key 、value组成。消息头部包含CRC,版本号,属性,时间戳,键长度,消息体长度来组成。
Key:消息键,对消息做partition使用,即消息保存在哪个partition。
Value:消息体,真实存储的消息。
Timestamp:消息发送时间戳,没传递默认当前时间。
另外这里单独提消息属性字段,kafka为他分配一个字节,目前只用最低3位保存消息压缩类型,其余5位未使用,当前只支持4中压缩类型:0(无压缩)、1(GZIP)、2(Snappy)、3(LZ4)。
其次,kafka使用紧凑的二进制字节数组来保存上面的字段,没有浪费多余的比特位,试想如果用java对象来保存,则堆内存开销较大,在java内存模型(java memory model,JVM)中,对象保存的开销其实非常大,对于小对象,通常花费2倍的空间来保存数据,随着数据越来越大,GC性能也会下降,拖慢系统吞吐量。
尽管JVM会对上面的类做优化,重排各个字段在内存的布局可以减少内存使用量,但还是会耗费很多字节,更糟的是,java系统默认开启页缓存,也就是说堆上保存的对象,在页缓存也会保留一份。
因此kafka设计消息的时候直接避开堆内存分配,直接采用紧凑的二进制byteBuffer,而不是独立的对象,因为我们至少能多一倍的内存可用。按kafka官网的说法,一台32g的内存机器,kafka几乎可用28~32g的内存。同时还不比担心GC糟糕性能。同时,使用页缓存而不是堆内存的好处是,当kafka broker宕机时候,数据不会消息,而堆内存的数据会消失。
1.2、topic和partiton
通常我们一个topic对应一个业务,比如A业务对应的topicA,B业务对应topicB,kafka的topic通常会被多个消费者订阅。
出于对性能的考虑,kafka并不是kafka-message格式的,而是topic-partition-message,中间多了一个partition,而消息存入partition是有序且不可修改的,每个partition都有专属的partition号,从0开始,用户唯一能做的就是从尾部增加消息,kafka每个消息都会分配唯一的序列号。按照kafka术语,这个序列号是位移(offset)。
1.3、offset
kafka的每一条消息有唯一的offset,其实消费端也有offset,与这个offset不能混为一谈,消费端的offset会随着消费,这个位子不断前移,因此以后如果谈论offset,则需要考虑是消费端的offset还是消息自己唯一的offset。所以可以确定,kafka的一条消息,其实就是由topic-partition-offset来组成唯一性的。
1.4、replica
如何保证数据不会丢失呢,这时候kafka的replica就体现出来了,我们为了防止数据丢失,其实还是用冗余机制----存储多份相同的数据来实现的,这时候一个broker宕机,数据全部丢失了。所以数据replica存在不同的broker上,当数据其中一个broker宕机的时候,其他的broker还有备份数据,如何实现的呢,是通过follower 和leader来实现的。
1.5、follower 和 leader
所以replica分为两个follower 和 leader,跟以前的主从完全不同,比如现在的leader 和 follower则只对外暴露leader提供服务,follower只是被动跟随leader修改自身的状态,保持与leader同步,只有当leader宕机的时候,这时候才会重新选举新的leader接替他的工作。
1.6、ISR
ISR全程就是in-sync-replica,翻译过来就是leader replica保持同步的replica集合。这里有一个重要的概念,比如一个partition可以配置N个replica,那么是否意味着该partition可以容忍n-1个replica宕机而数据不会丢失呢?当然不是。
Kafka为partition维护一个动态的replica集合,该集合的所有replica都与leader保持数据同步,也只有该集合里才可以被选举成为leader,只有当replica都同步成功消息,这时候kafka才会吧这条消息维护成 “已提交”状态,才会认为这条消息发送成功。记住这个关键:1、ISR中至少有一个活着的replica。2、“已提交”消息。所以用户经常抱怨kafka发消息失败,消息不存在,造成数据丢失。这是因为没满足上面两个条件,只有保证活着ISR,并且已提交才会存储。
正常情况下,所有replica(包含leader replica)都应该会保持同步,但会因为各种各样的原因,一小部分replica不能同步,落后进度,这时候会kafka会将一些replicat提出ISR。相反的,当这些replica重新追上leader进度时候,又会重新加入,这些都是kafka自身维护的,不需要人工干预。
2.1、消息传输
kafka非常使用于消息传输,这点大家毋庸置疑,具备更高的吞吐量,更低的延迟,其内置的分区机制保证了高可用性和高容错率。因此kafka非常适用于大数据的处理。
2.2、网站行为日志跟踪
kafka最早用于数据跟踪,多个网站用户操作,用消息的形式发送到kafka,这些点击蕴含巨大商业价值,因为这种点击量很大,于是kafka的高吞吐有了用武之地。
2.4、审计数据收集
因为kafka的持久化特性,让离线审计成为可能。
2.5、日志收集
这应该是kafka最常见的使用,每个企业都有大量的服务日志,这些日志分散在不同的服务器上,我们可以用kafka对他们进行全量收集。
2.6、event sourcing
与ddd(domain driven design)的设计模式不谋而合,event sourcing实际是领域驱动的名词,他使用事件序列来保持状态变更,这种思想与kafka不谋而合,前面也说过kafka用不可变的有序消息来存储。