做一个kafka的要点总结,官网或者博客都有大量资料
目录
Apache Kafka是一种发布订阅消息系统,一类消息被广播给所有订阅了该主题的用户,它是一种分布式的,分区化的,提供数据冗余以及持久化的日志服务。
Kafka支持海量数据的高速读写,一般在公司用做海量数据总线,连接后续的hadoop组件等,形成海量数据流。
基础术语
topic: 订阅主题,一般指一类消息。每个topic分成多个partition(分区), 每个分区都是一个有序的不可变的记录序列,不断追加到结构化日志中(持久化)。分区中每个记录都分配了一个偏移量ID,顺序递增,它唯一地标识分区中的每个记录。
partition: 分区,每个主题有1个至多个分区
消费者: 负责订阅消息,对象为KafkaConsumer,客户端接口。
消费者组: 一个分区只能被同一个消费组(ConsumerGroup)内的一个消费者消费
最基本的调用方式如下:
Properties props = new Properties();
props.put("bootstrap.servers", "ip:port");//kafka集群地址
props.put("group.id", "test"); //消费者组名称props.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer"); //kafka消息key的反序列化方式
props.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer"); //kafka消息value的反序列化方式
KafkaConsumer<String, String> comsumer = new KafkaConsumer<>(props);…
kafkaConsumer.subscribe(Lists.newArrayList("ORDER-DETAIL"));
try {
//消费者不断轮询, 否则该消费者会被认为死亡,它处理的分区被交给同组的其他消费者
while(true) {
ConsumerRecords<String, String> records = kafkaConsumer.poll(1000);
for (ConsumerRecord<String, String> record : records){
handle reocord.key and record.value
}
kafkaConsumer.commitAsync();
}
} finally {
kafkaConsumer.commitSync(); // 消费者关闭之前调用同步提交,更新消费偏移量
kafkaConsumer.close(); }
生产者:往topic中生产消息,客户端接口,消息对象为ProducerRecord。最基本调用方式如下:
Properties props = new Properties();
props.put("bootstrap.servers", "ip:port");//kafka集群地址
props.put("acks", "all"); //消息确认发送成功的机制,all表示leader和所有副本结点都确认
props.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");//kafka消息key的序列化方式
props.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");//kafka消息value的序列化方式
// ProducerRecord有很多不同的入参,可以通过源码org.apache.kafka.clients.producer.ProducerRecord进一步了解
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
for (int i = 0; i < 100; i++){
producer.send(new ProducerRecord<String, String>("my-topic",Integer.toString(i), Integer.toString(i)));
}
producer.close();
副本机制
kafka的副本机制(数据冗余)为数据传输保证了可靠性。kafka里的数据都都会保存不止一份,冗余的数据成为副本(Replica)。副本数是创建topic时必须指定的,topic每个分区都有N个副本,并且副本的个数一定小于broker个数。Kafka默认将副本均匀分布到不同的broker上,以保证某一台broker宕机之后,数据不会发生丢失。每个副本用broker-id表示。
Kafka数据备份
假设topic的副本数为3。kafka会选举其中的某一个分区为leader,另外两个为follower。在进行数据备份时,不是leader主动将数据push给follower,而是follower去向leader pull数据过来。当kafka的consumer去消费数据的时候,也是向leader里消费数据。follower只是起到备份数据的作用,定时从leader同步数据。所以,kafka的读和写都是通过leader的,follower只起备份作用。
Leader选举
ISR集合方法
Kafka会在Zookeeper上针对每个Topic维护一个称为ISR(in-sync replica,已同步的副本)的集合,该集合中是一些分区的副本。只有当这些副本都跟Leader中的副本同步了之后,kafka才会认为消息已提交,并反馈给消息的生产者。如果这个集合有增减,kafka会更新zookeeper上的记录。如果某个分区的Leader不可用,Kafka就会从ISR集合中选择一个副本作为新的Leader。
少数服从多数方法
少数服从多数是一种比较常见的一致性算法和Leader选举法。它的含义是只有超过半数的副本同步了,系统才会认为数据已同步;选择Leader时也是从超过半数的同步的副本中选择。这种算法需要较高的冗余度。譬如只允许一台机器失败,需要有三个副本;而如果只容忍两台机器失败,则需要五个副本。而kafka的ISR集合方法,分别只需要两个和三个副本。显然通过ISR,kafka需要的冗余度较低,可以容忍的失败数比较高。因此使用ISR集合方法。
如果所有的ISR副本都失败了怎么办
此时有两种方法可选,一种是等待ISR集合中的副本复活,一种是选择任何一个立即可用的副本,而这个副本不一定是在ISR集合中。这两种方法各有利弊,实际生产中按需选择。如果要等待ISR副本复活,虽然可以保证一致性,但可能需要很长时间。而如果选择立即可用的副本,则很可能该副本并不一致。
Kafka数据commit
producer发布数据有两种方式,分别是同步producer和异步producer。同步的意思就是,producer 发布数据后,需要成功后,才能发布下一条数据(producer发布数据到leader之后, 需要等所有的follower拉取到该数据后,leader才能commit)。而异步的意思是,producer发布数据后,无需等待即可发送下一条(producer发送数据到leader后,leader马上commit,之后follower自己慢慢的去pull数据)。
ISR配置
server配置:
replica.lag.time.mac.ms:超时时间,即当follower落后于leader的时间超过这个设定的时间后,则将这个follower从ISR移除。同理,如果发现不在ISR中的follower与leader的时间差小于这个值,则将其移动到ISR中。
replica.lag.max.messages:数据大小,即当follower落后leader的消息条数超过这个设置时,则将这个follower从ISR中移除。同理,如果发现不在ISR中的follower与leader相差的消息条数小于这个值,则将其移动到ISR中。
topic配置:
min.insync.replicas:ISR中最少的follower个数。一般设置成大于1的,否则如果最后的一个broker挂了,则数据就丢失了。
producer配置:
request.required.acks: 0--producer发送完数据后,不等待broker确认即发送下一条数据。 1--producer发送完数据后,等到leader确认后,发送下一条数据。 -1--producer等待ISR中的follower确认后发送下一条数据)
宕机如何恢复
少部分副本宕机
当leader宕机了,会从follower选择一个作为leader。当宕机的重新恢复时,会把之前commit的数据清空,重新从leader里pull数据。
全部副本宕机
1、等待ISR中的一个恢复后,并选它作为leader。(等待时间较长,降低可用性)
2、选择第一个恢复的副本作为新的leader,无论是否在ISR中。
Zookeeper协调服务
Zookeeper是一个开放源码的、高性能的协调服务,它被用于Kafka的分布式应用。zookeeper主要是为了统一分布式系统中各个节点的工作状态,在资源冲突的情况下协调提供节点资源抢占,提供给每个节点了解整个集群所处状态的途径。这一切的实现都依赖于zookeeper中的事件监听和通知机制。
在Kafka中,它被用于提交偏移量,因此如果节点在任何情况下都失败了,它都可以从之前提交的偏移量中获取。除此之外,它还执行其他活动,如: leader检测、分布式同步、配置管理、识别新节点何时离开或连接、集群、节点实时状态等等。
Zookeeper的watch机制
该机制是zookeeper的核心,此处只是略写,还有很多内容可以进一步研究。
每个kafka消费者组都对应着zookeeper中的一个文件夹;文件夹中是使用该消费者组,消费过的不同topic,也对应不同文件夹;topic文件夹下,就是该topic的每个分区文件。举个例子,通过group-a这个消费者组,消费了topic-a的消息,其中topic-a这个topic中包含3个分区,那么,通过zkCli客户端会看到如下三个文件:
…/group-a/topic-a/0
…/group-a/topic-a/1
…/group-a/topic-a/2
这三个文件都是zookeeper的一个znode节点,文件内就保存着消费的偏移量记录,当Znode发生变化(假设消费的偏移量增加),可以通过Watch机制通知到客户端。
要实现Watch,就必须实现org.apache.zookeeper.Watcher接口,并且将实现类的对象传入到可以Watch的方法中。Zookeeper中所有读操作(getData(),getChildren(),exists())都可以设置Watch选项。Watch事件具有one-time trigger(一次性触发)的特性,如果Watch监视的Znode有变化,那么就会通知设置该Watch的客户端,通过这样的机制,就可以保持偏移量的不断更新。
Kafka分区策略
Kafka目前支持三种分区策略RangeAssignor,RoundRobinAssigno,StickyAssignor,此处也是简单概括,可以进一步研究。
RangeAssignor分区策略
RangeAssignor策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。对于每一个topic,RangeAssignor策略会将消费组内所有订阅这个topic的消费者按照名称的字典序排序,然后为每个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分区。
简单来说就是分区总数对消费者数量进行取模,获得一个index,index为0就分配给第一个消费者,index为1就分配给第二个消费者,依次类推。假设n=分区数/消费者数量,m=分区数%消费者数量,那么前m个消费者每个分配n+1个分区,后面的(消费者数量-m)个消费者每个分配n个分区。
RoundRobinAssignor分区策略
RoundRobinAssignor策略的原理是将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询方式逐个将分区以此分配给每个消费者。RoundRobinAssignor策略对应的partition.assignment.strategy参数值为:org.apache.kafka.clients.consumer.RoundRobinAssignor。
StickyAssignor分区策略
Kafka从0.11.x版本开始引入这种分配策略,它主要有两个目的:
分区的分配要尽可能的均匀;
分区的分配尽可能的与上次分配的保持相同。
当两者发生冲突时,第一个目标优先于第二个目标。鉴于这两个目标,StickyAssignor策略的具体实现要比RangeAssignor和RoundRobinAssignor这两种分配策略要复杂很多。具体可以参考这篇文章:http://moguhu.com/article/detail?articleId=142
Kafka性能调优