分布式消息系统kafka的提供了一个生产者、缓冲区、消费者的模型html
kafka将全部消息组织成多个topic的形式存储,而每一个topic又能够拆分红多个partition,每一个partition又由一个一个消息组成。每一个消息都被标识了一个递增序列号表明其进来的前后顺序,并按顺序存储在partition中。java
这样,消息就以一个个id的方式,组织起来。node
这个id,在kafka中被称为offsetreact
这种组织和处理策略提供了以下好处:git
消息以partition为单位分配到多个server,并以partition为单位进行备份。备份策略为:1个leader和N个followers,leader接受读写请求,followers被动复制leader。leader和followers会在集群中打散,保证partition高可用github
producer生产消息须要以下参数:算法
根据kafka源码,能够根据不一样参数灵活调整生产、分区策略apache
if topic is None throw Error p=None if partition Not None if partition < 0 Or partition >= numPartitions throw Error p=partition elif key Not None p=hash(key) % numPartitions else p=round-robin() % numPartitions send message to the partition p
上面是我翻译的伪代码,其中round-robin就是简单轮询,hash采用的是murmurhashapi
传统消息系统有两种模式:缓存
kafka经过consumer group将两种模式统一处理
每一个consumer将本身标记consumer group名称,以后系统会将consumer group按名称分组,将消息复制并分发给全部分组,每一个分组只有一个consumer能消费这条消息。
因而推理出两个极端状况:
多consumer并发消费消息时,容易致使消息乱序
经过限制消费者为同步,能够保证消息有序,可是这大大下降了程序的并发性。
kafka经过partition的概念,保证了partition内消息有序性,缓解了上面的问题。partition内消息会复制分发给全部分组,每一个分组只有一个consumer能消费这条消息。这个语义保证了某个分组消费某个分区的消息,是同步而非并发的。若是一个topic只有一个partition,那么这个topic并发消费有序,不然只是单个partition有序。
通常消息消息系统,consumer存在两种消费模型:
kafka采用pull,并采用可配置化参数保证当存在数据而且数据量达到必定量的时候,consumer端才进行pull操做,不然一直处于block状态
kakfa采用整数值consumer position来记录单个分区的消费状态,而且单个分区单个消息只能被consumer group内的一个consumer消费,维护简单开销小。消费完成,broker收到确认,position指向下次消费的offset。因为消息不会删除,在完成消费,position更新以后,consumer依然能够重置offset从新消费历史消息
producer视角
consumer视角
在kafka中,正常状况下全部node处于同步中状态,当某个node处于非同步中状态,也就意味着整个系统出问题,须要作容错处理
同步中表明了:
某个分区内同步中的node组成一个集合,即该分区的ISR
kafka经过两个手段容错:
另外,kafka有个保障:当producer生产消息时,只有当消息被全部ISR确认时,才表示该消息提交成功。只有提交成功的消息,才能被consumer消费
综上所述:当有N个副本时,N个副本都在ISR中,N-1个副本都出现异常时,系统依然能提供服务
假设N副本全挂了,node恢复后会面临同步数据的过程,这期间ISR中没有node,会致使该分区服务不可用。kafka采用一种降级措施来处理:选举第一个恢复的node做为leader提供服务,以它的数据为基准,这个措施被称为脏leader选举。
因为leader是主要提供服务的,kafka broker将多个partition的leader均分在不一样的server上以均摊风险
每一个parition都有leader,若是在每一个partition内运行选主进程,那么会致使产生很是多选主进程。kakfa采用一种轻量级的方式:从broker集群中选出一个做为controller,这个controller监控挂掉的broker,为上面的分区批量选主
上面的方案保证了数据高可用,有时高可用是体如今对一致性的牺牲上。若是但愿达到强一致性,能够采起以下措施:
基于如下几点事实,kafka重度依赖磁盘而非内存来存储消息
在持久化数据结构的选择上,kafka采用了queue而不是Btree
kafka在如下四点作了优化:
大量读写少许消息会致使性能较差,经过将消息聚合,能够减小读写次数(减小随机IO),增长单次读写数据量(增长顺序IO)
普通状况下,数据从磁盘传输到网络须要经历如下步骤:
利用sendfile系统调用,能够简化至:
减小了两次拷贝步骤。在存在大量数据传输的操做时,会显著提高性能
在大量文件读写的时候,基于queue的read和append只须要一次磁盘寻址,而Btree则会涉及屡次。磁盘寻址过程极大下降了读写性能
kafka server端采用与Mina同样的网络、线程模型。server端基于nio,采用1个acceptor线程接受tcp链接,并将链接分配给N个proccessor线程,proccessor线程执行具体的IO读写、逻辑处理操做。(注:相比较于这种模型,netty的N boss + N worker的模型更加灵活)
broker node在zookeeper中采用惟一id(整数)标识
/brokers/ids/[N] --> host:port 瞬时节点
此znode存储了broker node的ip端口
/brokers/topics/[topic]/partitions/[N]/state --> leader,isr 瞬时节点
此znode存储了该分区的leader id和isr列表(由id组成)
/consumers/[group_id]/ids/[customer_id] --> {"topic1": #streams, ..., "topicN": #streams} 瞬时节点
此znode存储了指定consumer消费topic所使用的线程数
/consumers/[group_id]/offsets/[topic]/[N] --> offset 永久节点
consumer能够经过三种方式管理offset:
此znode存储了指定consumer在topic中最新consumer offset
/consumers/[group_id]/owners/[topic]/[N] --> consumer_id 瞬时节点
指定分区在某一时刻只能被全部consumer group中的某一个consumer消费,经过将consumer_id存在指定分区下,就能保证这时该分区只能被这个consumer消费
上面只是列出的最典型的znode,经过研究znode,能够开发出一个kafka monitor,用来监控kafka数据消费情况,好比KafkaOffsetMonitor