kafka知识点整理总结
kafka知识点整理总结,以备不时之需。
为什么要使用消息系统:
-
解耦
-
并行
-
异步通信:想向队列中放入多少消息就放多少,然后在需要的时候在去处理它们
-
冗余 :数据持久化直到他们被完全处理,防止数据丢失
-
扩展性:
-
灵活性 & 峰值处理能力 : 峰值期,消息队列提供缓冲的作用,削峰
-
可恢复性 : 系统一部分组件失效,不会影响整个系统
-
顺序保证 : kafka 保证一个Partition内的消息的有序性
-
缓冲:通过一个缓冲层来帮助任务最高效率的执行
kafka介绍
kafka: 使用scala编写,因 可水平扩展和高吞吐率 而被广泛使用。
kafka是一种分布式,基于发布/订阅的消息系统,主要设计目标如下:
-
以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能保证常数时间复杂度访问性能。
-
高吞吐率。即使再非常廉价的商用机器上也能做到单机支持每秒100k以上消息的传输
-
支持kafka Server间的消息分区,及分布式消费,同时保证每个Partition内的消息顺序传输。
-
同时支持离线数据处理和实时数据处理
-
scale out:支持水平扩展
kafka架构
组成
-
Broker:kafka集群包含一个或多个服务器(节点),这种服务器被称为broker
-
Topic:每条发布到Kafka集群的消息的类别。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker,但用户只需指定消息的topic,即生产或消费数据不必关心数据存于何处)
-
Partition:partition是物理概念,每个Topic包含一个或多个partition
-
Producer:负责发布消息到kafka broker
-
Consumer :消息消费者,从kafka broker读取消息
-
Consumer Group:每个consumer属于一个特定的Consumer Group(可为每个COnsumer指定Group name若不指定,则属于默认Group)
Zookeeper:集群保存meta信息。
kafka文件结构:
为了使得kafka的吞吐率可以线性提高,物理上把Topic分成多个Partition,每个Partition物理上对应一个文件,该文件夹下存储这个partition的所有消息和索引文件。(假设两个topic,topic1和topic2。分区文件名 为topic_name+partition_num,即:topic1-1..topic1-5)。
partition下的文件并非由一个文件构成,而是分成多个segment,另外会有一个索引文件。Segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。
以起始偏移量命名并排序这些文件,只要根据offset **二分查找**文件列表,就可以快速定位到具体文件。
每条消息都被append到该Partition中,属于顺序写磁盘,因此效率非常高(这是Kafka高吞吐率的一个很重要的保证)。
文件结构
segment文件命名格式
kafka消息持久化
Kafka集群会保留所有的消息,无论其被消费与否。当然,因为磁盘限制,不可能永久保留所有数据(实际上也没必要),因此Kafka提供两种策略删除旧数据:一、基于时间,二基于partition文件大小
可以通过配置$KAFKA_HOME/config/server.properties中
# The minimum age of a log file to be eligible for deletionlog.retention.hours=168# The maximum size of a log segment file. When this size is reached a new log segment will be created.log.segment.bytes=1073741824# The interval at which log segments are checked to see if they can be deleted according to the retention policieslog.retention.check.interval.ms=300000# If log.cleaner.enable=true is set the cleaner will be enabled and individual logs can then be marked for log compaction.log.cleaner.enable=false
这里要注意,因为Kafka读取特定消息的时间复杂度为O(1),即与文件大小无关,所以这里删除过期文件与提高Kafka性能无关。选择怎样的删除策略只与磁盘以及具体的需求有关。另外,Kafka会为每一个Consumer Group保留一些metadata信息——当前消费的消息的position,也即offset。这个offset由Consumer控制。正常情况下Consumer会在消费完一条消息后递增该offset。当然,Consumer也可将offset设成一个较小的值,重新消费一些消息。因为offet由Consumer控制,所以Kafka broker是无状态的,它不需要标记哪些消息被哪些消费过,也不需要通过broker去保证同一个Consumer Group只有一个Consumer能消费某一条消息,因此也就不需要锁机制,这也为Kafka的高吞吐率提供了有力保障。
producer消息路由:
roducer发送消息到broker时,会根据Paritition机制选择将其存储到哪一个Partition。有了Partition后,不同的消息可以并行写入不同broker的不同Partition里,极大的提高了吞吐率(分区的作用:负载均衡,提升topic的并发消费能力)可以在$KAFKA_HOME/config/server.properties中通过配置项num.partitions来指定新建Topic的默认Partition数量,也可在创建Topic时通过参数指定,同时也可以在Topic创建之后通过Kafka提供的工具修改。
在发送一条消息时,可以指定这条消息的key,Producer根据这个key和Partition机制来判断应该将这条消息发送到哪个Parition。Paritition机制可以通过指定Producer的paritition. class这一参数来指定,该class必须实现kafka.producer.Partitioner接口
使用Consumer high level API时,同一Topic的一条消息只能被同一个Consumer Group内的一个Consumer消费,但多个Consumer Group可同时消费这一消息。
kafka集群中的任何一个broker都可以向producer提供metadata信息,这些metadata中包含"集群中存活的servers列表"/"partitions leader列表"等信息;
当producer获取到metadata信息之后, producer将会和Topic下所有partition leader保持socket连接;
Consumer Group与单播和广播
这是Kafka用来实现一个Topic消息的广播(发给所有的Consumer)和单播(发给某一个Consumer)的手段。一个Topic可以对应多个Consumer Group。如果需要实现广播,只要每个Consumer有一个独立的Group就可以了。要实现单播只要所有的Consumer在同一个Group里。用Consumer Group还可以将Consumer进行自由的分组而不需要多次发送消息到不同的Topic。
kafka副本机制
kafka使用的是默认副本 — 就是不需要副本的topic的复制因子就是1。
kafka每个topic的partition有N个副本,其中N是topic的复制因子。kafka通过多副本机制实现故障自动转移。N个replicas中,其中一个replica为leader,其他都为follower,leader处理partition的所有读写请求,与此同时,follower会被动定期地去复制leader上的数据
leader负责维护和跟踪ISR中所有follower滞后状态。当生产者发送一条消息到Broker,leader写入消息并复制到所有follower。消息提交之后才被成功复制到所有的同步副本。消息复制延迟受最慢的follower限制,重要的是快速检测慢副本,如果follower”落后”太多或者失效,leader将会把它从replicas从ISR移除。
请看一个例子:主题名称为foo 1 partition 3 replicas。假如partition的replication分布在Brokers 1、2和3上,并且Broker 3消息已经成功提交。同步副本列表中1为leader、2和3为follower。假设replica.lag.max.messages设置为4,表明只要follower落后leader不超过3,就不会从同步副本列表中移除。replica.lag.time.max设置为500 ms,表明只要follower向leader发送请求时间间隔不超过500 ms,就不会被标记为死亡,也不会从同步副本列中移除
一个partition的follower落后于leader足够多时,被认为不在同步副本列表或处于滞后状态。在Kafka-0.8.2.x中,副本滞后判断依据是副本落后于leader最大消息数量(replica.lag.max.messages)或replicas响应partition leader的最长等待时间(replica.lag.time.max.ms)。前者是用来检测缓慢的副本,而后者是用来检测失效或死亡的副本
主题在流量高峰期发送了一批消息(4条消息),等于replica.lag.max.messages = 4配置值。在那一瞬间,2个follower replica将被认为是”out-of-sync”并且leader会从同步副本列表中移除。 2个follower replica都是活着,下次拉取请求他们会赶上leader log end offset并重新加入同步副本列表。重复相同的过程,如果生产者继续发送相对一批较大消息到leader。这种情况演示了当follower replica频繁在从同步副本列表移除和重新加入同步副本列表之间来回切换时,不必要触发虚假警报。
副本配置规则
笔者认为真正重要的事情是检测卡或慢副本,这段时间follower replica是“out-of-sync”落后于leader。在服务端现在只有一个参数需要配置replica.lag.time.max.ms。除非replica请求leader时间间隔大于replica.lag.time.max.ms,因此即使leader使流量激增和大批量写消息。Kafka也不会从同步副本列表从移除该副本。
对于kafka的节点活着有2个条件:
A node must be able to maintain its session with ZooKeeper (via ZooKeeper's heartbeat mechanism)
一个节点必须能维持与zookeeper的会话(通过zookeeper的心跳机制)
If it is a slave it must replicate the writes happening on the leader and not fall "too far" behind
如果它是一个slave,它必须复制leader并且不能落后"太多"
我们让节点满足这2个“同步”条件,以区分“活着”还是“故障”。leader跟踪“同步”节点。如果一个follower死掉,卡住,或落后,leader将从同步副本列表中移除它。落后是通过replica.lag.max.messages配置控制,卡住是通过replica.lag.time.max.ms配置控制的。
kafka Quorums leader选举
为了保证较高的处理效率,消息的读写都是在固定的一个副本上完成。这个副本就是所谓的Leader,而其他副本则是Follower。而Follower则会定期地到Leader上同步数据。
如果leader没有故障,我们就不需要follower!当leader确实故障了,我们需要从follower中选出新的leader,但是follower自己可能落后或崩溃,所以我们必须选择一个最新的follower。日志复制算法必须提供保证,如果我们告诉客户端消息是已发送,leader故障了,我们选举的新的leader必须要有这条消息,这就产生一个权衡:如果leader等待更多的follwer声明已提交之前,应答消息的话,将会有更多有资格的leader。
如果你选择需要应答数量必须和日志的数量进行比较,选出一个leader。这样保证有重叠,那么这就是所谓的Quorum(法定人数)。
一种常见的方法,用多数投票决定leader选举。kafka不是这样做的,但先让我们了解这个权衡,假如,我们有2f+1副本,如果f+1副本在leader提交之前必须收到消息,并且如果我们选举新的leader,至少从f+1副本选出最完整日志的follwer,并且不大于f的失败,leader担保所有已提交的信息。这是因为任何f+1副本中,必须至少有一个副本,其中包含所有已提交的消息。该副本的日志是最完整的,因此选定为新的leader。这种算法需要较高的冗余度。譬如只允许一台机器失败,需要有三个副本;而如果只容忍两台机器失败,则需要五个副本。而kafka的ISR集合方法,分别只需要两个和三个副本。
kafka采用了一种稍微不同的方法选择quorum,而不是多数投票,Kafka会在Zookeeper上针对每个Topic维护一个称为ISR(in-sync replica,已同步的副本)的集合,该集合中是一些分区的副本。只有当这些副本都跟Leader中的副本同步了之后,kafka才会认为消息已提交,并反馈给消息的生产者。如果这个集合有增减,kafka会更新zookeeper上的记录。正因为如此,在ISR中的任何副本都有资格当选leader,这是kafka的使用模型,有多个分区和确保leader平衡是很重要的一个重要因素。有了这个模型,ISR和f+1副本,kafka的主题可以容忍f失败而不会丢失已提交的消息。
如果所有的ISR副本都失败了怎么办
此时有两种方法可选,一种是等待ISR集合中的副本复活,一种是选择任何一个立即可用的副本,而这个副本不一定是在ISR集合中。这两种方法各有利弊,实际生产中按需选择。
如果要等待ISR副本复活,虽然可以保证一致性,但可能需要很长时间。而如果选择立即可用的副本,则很可能该副本并不一致。
kafka顺序写入
不同于Redis和MemcacheQ等内存消息队列,Kafka的设计是把所有的Message都要写入速度低容量大的硬盘,以此来换取更强的存储能力。实际上,Kafka使用硬盘并没有带来过多的性能损失,“规规矩矩”的抄了一条“近道”。
首先,说“规规矩矩”是因为Kafka在磁盘上只做Sequence I/O,由于消息系统读写的特殊性,这并不存在什么问题。关于磁盘I/O的性能,引用一组Kafka官方给出的测试数据(Raid-5,7200rpm):
Sequence I/O: 600MB/s
Random I/O: 100KB/s
Kafka重度依赖底层操作系统提供的PageCache功能。当上层有写操作时,操作系统只是将数据写入PageCache,同时标记Page属性为Dirty。
当读操作发生时,先从PageCache中查找,如果发生缺页才进行磁盘调度,最终返回需要的数据。实际上PageCache是把尽可能多的空闲内存都当做了磁盘缓存来使用。同时如果有其他进程申请内存,回收PageCache的代价又很小,所以现代的OS都支持PageCache。
PageCache还只是第一步,Kafka为了进一步的优化性能还采用了Sendfile技术在解释Sendfile之前,首先介绍一下传统的网络I/O操作流程,大体上分为以下4步:
OS 从硬盘把数据读到内核区的PageCache。
用户进程把数据从内核区Copy到用户区。
然后用户进程再把数据写入到Socket,数据流入内核区的Socket Buffer上。
OS 再把数据从Buffer中Copy到网卡的Buffer上,这样完成一次发送。
整个过程共经历两次Context Switch,四次System Call。同一份数据在内核Buffer与用户Buffer之间重复拷贝,效率低下。其中2、3两步没有必要,完全可以直接在内核区完成数据拷贝。这也正是Sendfile所解决的问题,经过Sendfile优化后,整个I/O过程就变成了下面这个样子。
通过以上的介绍不难看出,Kafka的设计初衷是尽一切努力在内存中完成数据交换,无论是对外作为一整个消息系统,或是内部同底层操作系统的交互。如果Producer和Consumer之间生产和消费进度上配合得当,完全可以实现数据交换零I/O。这也就是我为什么说Kafka使用“硬盘”并没有带来过多性能损失的原因。下面是我在生产环境中采到的一些指标。
(20 Brokers, 75 Partitions per Broker, 110k msg/s)
其他
Kafka分配Replica的算法如下:
将所有Broker(假设共n个Broker)和待分配的Partition排序
将第i个Partition分配到第(imod n)个Broker上
将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上
Kafka常用操作命令
l 查看当前服务器中的所有topic
bin/kafka-topics.sh --list --zookeeper node01:2181
l 创建topic
bin/kafka-topics.sh --create --zookeeper node01:2181 --replication-factor 1 --partitions 1 --topic test
l 删除topic
bin/kafka-topics.sh --delete --zookeeper node01:2181 --topic test
需要server.properties中设置delete.topic.enable=true否则只是标记删除或者直接重启。
l 通过shell命令发送消息
bin/kafka-console-producer.sh --broker-list node01:9092 --topic test1
l 通过shell消费消息
bin/kafka-console-consumer.sh --zookeeper node01:2181 --from-beginning --topic test1
l 查看消费位置
bin/kafka-run-class.sh kafka.tools.ConsumerOffsetChecker –zookeeper node01:2181 --group testGroup
l 查看某个Topic的详情
bin/kafka-topics.sh --topic test --describe --zookeeper node01:2181
l 对分区数进行修改
bin/kafka-topics.sh --zookeeper node01 --alter --partitions 15 --topic utopic
整理自网络
链接:http://orchome.com/22 , http://blog.csdn.net/liuao107329/article/details/70213599 ,http://www.infoq.com/cn/articles/kafka-analysis-part-1等