Kafka的基本介绍nginx
Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也能够当作MQ系统),常见能够用于web/nginx日志、访问日志、消息服务等等。web
主要应用场景:日志收集系统和消息系统。算法
主要设计目标:网络
一、以时间复杂度O(1)的方式提供消息持久化能力,即便对TB级以上数据也能保证常数时间的访问性能。session
二、高吞吐率。即便在很是廉价的商用机器上也能作到单机支持每秒100K条消息的传输。app
三、支持kafka server间的消息分区,及分布式消费,同时保证每一个partition内的消息顺序传输。负载均衡
四、同时支持离线数据处理和实时数据处理。socket
kafka设计原理分析分布式
一个典型的kafka集群中包含若干producer,若干broker,若干consumer,以及一个zookeeper集群。kafka经过zookeeper管理集群配置,选举leader,以及在消费组发送变化时进行rebalance。producer使用push模式将消息发布到broker,consumer使用pull模式从broker订阅并消费消息。函数
kafka专用术语:
一、broker:消息中间件处理结点,一个kafka节点就是一个broker,多个broker能够组成一个kafka集群。
二、Topic:一类消息,kafka集群可以同时负责多个topic的分发。
三、partition:topic物理上的分组,一个topic能够分为多个partition,每一个partition是一个有序的队列。
四、Segment:partition物理上由多个segment组成。
五、offset:每一个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。partition中的每一个消息都有一个连续的序列号叫作offset,用于partition惟一标识一条消息。
六、Producer:负责发布消息到Kafka broker。
七、Consumer:消息消费者,向Kafka broker读取消息的客户端。
八、Consumer Group:每一个Consumer属于一个特定的Consumer Group。
kafka消息存储格式
Topic & Partition
一个topic能够任务一个一类消息,每一个topic将被分红多个partition,每一个partition在存储层面是append log文件。
在kafka文件存储中,同一个topic下有多个不一样partition,每一个partition为一个目录,partition命名规则为:topic名称+有序序号,第一个partition序号从0开始,序号最大值为partition数量减1.
一、每一个partitin(目录)至关于一个巨型文件被平均分配到多个segment(段)数据文件中。但每一个段segment file消息数量不必定相等,这种特性方便old segment file快速被删除。
二、每一个partition只须要支持顺序读写就好了,segment文件生命周期由服务端配置参数决定。
上面两点这样作的好处就是能快速删除无用文件,有效提升磁盘利用率。
三、segment file组成:由2大部分组成,分别为index file和data file,此2个文件一一对应,成对出现,后缀".index"和“.log”分别表示为segment索引文件、数据文件.
四、segment文件命名规则:partion全局的第一个segment从0开始,后续每一个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。
segment中index与data file对应关系物理结构以下:
上图中索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址。
其中以索引文件中元数据3,497为例,依次在数据文件中表示第3个message(在全局partiton表示第368772个message),以及该消息的物理偏移地址为497。
副本(Replication)策略
kafka的高可靠性的保障来源于其健壮的副本(replication)策略。
一、数据同步
kafka在0.8版本前没有提供Partition的Replication机制,一旦Broker宕机,其上的全部Partition就都没法提供服务,而Partition又没有备份数据,数据的可用性就大大下降了。因此0.8后提供了Replication机制来保证Broker的failover(故障转移)。
引入Replication以后,同一个Partition可能会有多个Replica,而这时须要在这些Replication之间选出一个Leader,Producer和Consumer只与这个Leader交互,其它Replica做为Follower从Leader中复制数据。
二、副本放置策略:
为了更好的作负载均衡,kafka尽可能将全部的partition均匀分配到整个集群上。
kafka分配Replica的算法以下:
a、将全部存活的N个Brokers和待分配的Partition排序。
b、将第i个partition分配到第(i mod n)个Broker上,这个Partition的第一个Replica存在于这个分配的Broker上,而且会做为partition的优先副本
c、将第i个Partition的第j个Replica分配到第((i + j) mod n)个Broker上
假设集群一共有4个brokers,一个topic有4个partition,每一个Partition有3个副本。下图是每一个Broker上的副本分配状况。
三、同步策略
Producer在发布消息到某个Partition时,先经过Zookeeper找到该Partition的Leader,而后不管该Topic的Replication Factor为多少,Producer只将该消息发送到该Partition的Leader。Leader会将该消息写入其本地log。每一个Fllower都从Leader pull数据。这种方式上,Follower存储的数据顺序与Leader保持一致。Follower在收到该消息并写入其LOG后,向Leader发送ACK。一旦Leader收到了ISR中的全部Replica的ACK,该消息就被认为已经commit了,Leader将增长HW而且向Producer发送ACK。
为了提升性能,每一个Follower在接收到数据后就立马向Leader发送ACK,而非等到数据写入Log中。所以,对于已经commit的消息,Kafka只能保证它被存于多个Replica的内存中,而不能保证它们被持久化到磁盘中,也就不能彻底保证异常发生后该条消息必定能被Consumer消费。
Consumer读消息也是从Leader读取,只有被commit过的消息才会暴露给Consumer。
Kafka Replication的数据流以下图所示:
对于kafka而言,定义一个Broker是否“活着”包含两个条件:
一、一是它必须维护与Zookeeper的session(这个能够经过ookeeper的心跳机制来实现)
二、二是Follower必须可以及时将Leader的消息复制过来,不能“落户太多”
Leader会跟踪与其保持同步的Replica列表,该列表称为ISR(即in-sync Replica)。若是一个Follower宕机,或者落后太多,Leader将把它从ISR中移除。这里所描述的“落后太多”指Follower复制的消息落后于Leader后的条数超过预约值或者Follower超过必定时间未向Leader发送fetch请求。
Kafka只解决fail/recover,一条消息只有被ISR里的全部Follower都从Leader复制过去才会被认为已提交。这样就避免了部分数据被写进了Leader,还没来得及被任何Follower复制就宕机了,而形成数据丢失(Consumer没法消费这些数据)。而对于Producer而言,它能够选择是否等待消息commit。这种机制确保了只要ISR有一个或以上的Follower,一条被commit的消息就不会丢失。
四、Leader选举
Leader选举本质上是一个分布式锁,有两种方式实现基于ZooKeeper的分布式锁:
a、节点名称惟一性:多个客户端建立一个节点,只有成功建立节点的客户端才能得到锁
b、临时顺序节点:全部客户端在某个目录下建立本身的临时顺序节点,只有序号最小的才得到锁
kafka消息分组,消息消费原理
同一Topic的一条消息只能被同一个Consumer Group内的一个Consumer消费,但多个Consumer Group可同时消费这一消息。
这是Kafka用来实现一个Topic消息的广播(发给全部的Consumer)和单播(发给某一个Consumer)的手段。一个Topic能够对应多个Consumer Group。若是须要实现广播,只要每一个Consumer有一个独立的Group就能够了。要实现单播只要全部的Consumer在同一个Group里。用Consumer Group还能够将Consumer进行自由的分组而不须要屡次发送消息到不一样的Topic。
Push vs Pull
做为一个消息系统,Kafka遵循了传统的方式,选择由Producer向broker push消息并由Consumer从broker pull消息。
push模式很难适应消费速率不一样的消费者,由于消息发送速率是由broker决定的。push模式的目标是尽量以最快速度传递消息,可是这样很容易形成Consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而pull模式则能够根据Consumer的消费能力以适当的速率消费消息。
对于Kafka而言,pull模式更合适。pull模式可简化broker的设计,Consumer可自主控制消费消息的速率,同时Consumer能够本身控制消费方式——便可批量消费也可逐条消费,同时还能选择不一样的提交方式从而实现不一样的传输语义。
Kafka顺序写入与数据读取
生产者(producer)是负责向Kafka提交数据的,Kafka会把收到的消息都写入到硬盘中,它绝对不会丢失数据。为了优化写入速度Kafak采用了两个技术,顺序写入和MMFile(文件管理系统)。
顺序写入
由于硬盘是机械结构,每次读写都会寻址,写入,其中寻址是一个“机械动做”,它是最耗时的。因此硬盘最“讨厌”随机I/O,最喜欢顺序I/O。为了提升读写硬盘的速度,Kafka就是使用顺序I/O。
每条消息都被append到该Partition中,属于顺序写磁盘,所以效率很是高。
对于传统的message queue而言,通常会删除已经被消费的消息,而Kafka是不会删除数据的,它会把全部的数据都保留下来,每一个消费者(Consumer)对每一个Topic都有一个offset用来表示读取到了第几条数据。
即使是顺序写入硬盘,硬盘的访问速度仍是不可能追上内存。因此Kafka的数据并非实时的写入硬盘,它充分利用了现代操做系统分页存储来利用内存提升I/O效率。
在Linux Kernal 2.2以后出现了一种叫作“零拷贝(zero-copy)”系统调用机制,就是跳过“用户缓冲区”的拷贝,创建一个磁盘空间和内存空间的直接映射,数据再也不复制到“用户态缓冲区”系统上下文切换减小2次,能够提高一倍性能。
经过mmap,进程像读写硬盘同样读写内存(固然是虚拟机内存)。使用这种方式能够获取很大的I/O提高,省去了用户空间到内核空间复制的开销(调用文件的read会把数据先放到内核空间的内存中,而后再复制到用户空间的内存中。)
消费者(读取数据)
试想一下,一个Web Server传送一个静态文件,如何优化?答案是zero copy。传统模式下咱们从硬盘读取一个文件是这样的。
先复制到内核空间(read是系统调用,放到了DMA,因此用内核空间),而后复制到用户空间(一、2);从用户空间从新复制到内核空间(你用的socket是系统调用,因此它也有本身的内核空间),最后发送给网卡(三、4)。
Zero Copy中直接从内核空间(DMA的)到内核空间(Socket的),而后发送网卡。这个技术很是广泛,Nginx也是用的这种技术。
实际上,Kafka把全部的消息都存放在一个一个的文件中,当消费者须要数据的时候Kafka直接把“文件”发送给消费者。当不须要把整个文件发出去的时候,Kafka经过调用Zero Copy的sendfile这个函数,这个函数包括:
out_fd做为输出(通常及时socket的句柄)
in_fd做为输入文件句柄
off_t表示in_fd的偏移(从哪里开始读取)
size_t表示读取多少个
转自:http://www.linkedkeeper.com/detail/blog.action?bid=1016