文章目录
- 问题1、谈谈对kafka的理解
- 问题2、Kafka和sparkStreaming的整合,手动提交的offset调用了什么方法?
- 问题3、hive怎么消费kafka的数据的
- 问题4、kafka如何管理自身的offset
- 问题5、flume 如何保证数据的可靠性?
- 问题6、kafka如何保证数据不会出现丢失或者重复消费的情况?
- 问题7、kafka消费数据是怎么消费的,用的是什么方式?
- 问题8、Kafka为什么要分区,生产数据的分区策略
- 问题9、Kafka消费者的分区分配策略
- 问题10、传统的消息传递方法与kafka的不同
- 问题11、Kafka的ISR机制
- 问题12、如何保证Kafka的消息有序
- 问题13、Kafka的高吞吐怎么实现的
- 问题14、Kafka的工作流程分析
- 问题15、Kafka怎么解决数据积压
- 问题16、Kafka为什么写入性能很高
- 问题17、kafka申请topic命令
问题1、谈谈对kafka的理解
-
kafka是最初由linkedin公司开发的,使用scala语言编写,kafka是一个分布式,分区的,多副本的,多订阅者的日志系统(分布式MQ系统),可以用于搜索日志,监控日志,访问日志等。
-
apache kafka是一个分布式发布-订阅消息系统和一个强大的队列,可以处理大量的数据,并使能够将消息从一个端点传递到另一个端点,kafka适合离线和在线消息消费。kafka消息保留在磁盘上,并在集群内复制以防止数据丢失。kafka构建在zookeeper同步服务之上。它与apache和spark非常好的集成,应用于实时流式数据分析。
-
Broker:kafka集群中包含一个或者多个服务实例,这种服务实例被称为Broker
-
Topic:每条发布到kafka集群的消息都有一个类别,这个类别就叫做Topic
- kafka将消息以topic为单位进行归类
topic特指kafka处理的消息源(feeds of messages)的不同分类。
topic是一种分类或者发布的一些列记录的名义上的名字。kafka主题始终是支持多用户订阅的;也就是说,一 个主题可以有零个,一个或者多个消费者订阅写入的数据。
在kafka集群中,可以有无数的主题。
生产者和消费者消费数据一般以主题为单位。更细粒度可以到分区级别。
- kafka将消息以topic为单位进行归类
-
Partition:Partition是一个物理上的概念,每个Topic包含一个或者多个Partition
-
Producer:负责发布消息到kafka的Broker中。
-
Consumer:消息消费者,向kafka的broker中读取消息的客户端
-
Consumer Group:每一个Consumer属于一个特定的Consumer Group(可以为每个Consumer指定 groupName)
: 消费组: 由一个或者多个消费者组成,同一个组中的消费者对于同一条消息只消费一次。
问题2、Kafka和sparkStreaming的整合,手动提交的offset调用了什么方法?
问题3、hive怎么消费kafka的数据的
问题4、kafka如何管理自身的offset
- offset有两种存放地点,一种是存入一个topic中,该topic被分成50个分区。还有一种是存入zookeeper中,这种方式在javaApi中已经实现了
问题5、flume 如何保证数据的可靠性?
- Channel提供两种:memory Channel、file Channel。file Channel能把event持久化到磁盘,避免数据丢失
- Flume 使用事务的办法来保证 event 的可靠传递。Source 和 Sink 分别被封装在事务中,这些事务由保存 event 的存储提供或者由 Channel 提供。这就保证了 event 在数据流的点对点传输中是可靠的。
问题6、kafka如何保证数据不会出现丢失或者重复消费的情况?
- 保证数据不丢失可以从三方面考虑
- 生产者数据不丢失:
- kafka的ack机制可以保证数据发送不丢失,它有三个值 0 1 -1
- 0:生产者只负责发送数据,不关心数据是否丢失,响应的状态码为0
- 1:partition的leader收到数据,响应的状态码为1
- -1:所有的从节点都收到数据,响应的状态码为-1
- 从数据发送方面考虑的话。有异步和同步发送的方式
- 在同步的情况下:发送一批数据给kafka后,等待kafka返回结果(默认方式)
- 生产者等待10s,如果broker没有给出ack相应,就认为失败。生产者重试3次,如果还没有相应,就报错
- 异步:发送一批数据给kafka,只是提供一个回调函数。(在消息不是很重要的话,用异步)
- 先将数据保存在生产者端的buffer中。buffer大小是2万条
- 满足数据阈值或者数量阈值其中的一个条件就可以发送数据。
- 发送一批数据的大小是500条
- 在同步的情况下:发送一批数据给kafka后,等待kafka返回结果(默认方式)
- broker保证数据不丢失
- 主要是通过副本因子(冗余),防止数据丢失
- 消费者消费数据不丢失
- 只要每个消费者记录好offset值即可,就能保证数据不丢失。
- kafka的ack机制可以保证数据发送不丢失,它有三个值 0 1 -1
- 生产者数据不丢失:
- 保证不出现重复消费的话,要从消费者提交offset考虑(重复消费:consumer有些消息处理了,但是没来得及提交offset。等重启之后,少数消息就会再次消费一次。)
- 那么我们就需要从幂等性角度考虑了。幂等性,我通俗点说,就一个数据,或者一个请求,无论来多次,对应的数据都不会改变的,不能出错。
- 比如某个数据要写库,你先根据主键查一下,如果数据有了,就别插入了,update一下好吧
- 比如你是写redis,那没问题了,反正每次都是set,天然幂等性
- 对于消息,我们可以建个表(专门存储消息消费记录)
- 生产者,发送消息前判断库中是否有记录(有记录说明已发送),没有记录,先入库,状态为待消费,然后发送消息并把主键id带上。
- 消费者,接收消息,通过主键ID查询记录表,判断消息状态是否已消费。若没消费过,则处理消息,处理完后,更新消息记录的状态为已消费。
问题7、kafka消费数据是怎么消费的,用的是什么方式?
- 自动提交offset(At-most-once(最多一次))
- 客户端收到消息后,在处理消息前自动提交,这样kafka就认为consumer已经消费过了,偏移量增加。
//设置enable.auto.commit为true
props.put("enable.auto.commit", "true");
- 手动提交offset(At-least-once(最少一次))
- 可能出现消息处理完了,在提交offset前,网络中断或者程序挂了,那么kafka认为这个消息还没有被consumer消费,产生重复消息推送。
//关闭自动提交确认选项 props.put("enable.auto.commit", "false"); //手动提交offset值 kafkaConsumer.commitASync();
- Exactly-once(正好一次)
- 保证消息处理和提交反馈在同一个事务中,即有原子性。例如在同一个事务中,把消息处理的结果存到mysql数据库同时更新此时的消息的偏移。
- 考虑避免重复消费的解答
//设置enable.auto.commit为false //保存ConsumerRecord中的offset到数据库 //当partition分区发生变化的时候需要rebalance,有以下几个事件会触发分区变化 //consumer通过调用seek(TopicPartition, long)方法,移动到指定的分区的偏移位置。
Kafka消费数据的方式主要包含如下几种:
1、指定多主题消费
consumer.subscribe(Arrays.asList(“t4”,“t5”));
2、指定分区消费
consumer.assign(list);
3、手动修改偏移量
consumer.commitSync(); //提交当前消费偏移量
consumer.commitSync(Map) //提交指定偏移量
consumer.assign(Arrays.asList(tp));
4、seek,修改偏移量搜索指针,顺序读取数据
consumer.assign(Arrays.asList(tp));
consumer.seek(tp,0);
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
import org.apache.kafka.common.TopicPartition;
import java.util.*;
public class NewConsumer {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers","s102:9092,s103:9092,s104:9092");
props.put("group.id", "g3");
props.put("enable.auto.commit", "false");
props.put("auto.commit.interval.ms", "100");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer consumer = new KafkaConsumer(props);
//通过subscribe方法,指定多主题消费
//consumer.subscribe(Arrays.asList("t4","t5"));
//指定分区消费
// ArrayList list = new ArrayList();
// TopicPartition tp = new TopicPartition("t1", 0);
// TopicPartition tp2 = new TopicPartition("t4", 0);
// TopicPartition tp3 = new TopicPartition("t4", 1);
// TopicPartition tp4 = new TopicPartition("t4", 2);
// list.add(tp);
// list.add(tp2);
// list.add(tp3);
// list.add(tp4);
// consumer.assign(list);
Map offset = new HashMap();
//指定分区
TopicPartition tp = new TopicPartition("t4", 0);
//指定偏移量
OffsetAndMetadata metadata = new OffsetAndMetadata(3);
offset.put(tp,metadata);
//修改偏移量
consumer.commitSync(offset);
//订阅主题
//consumer.subscribe(Arrays.asList("t4"));
consumer.assign(Arrays.asList(tp));
//指定分区
// TopicPartition tp = new TopicPartition("t4", 0);
// consumer.assign(Arrays.asList(tp));
// //修改搜索指针
// consumer.seek(tp,10);
while (true) {
ConsumerRecords records = consumer.poll(5000);
for (ConsumerRecord record : records)
System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
consumer.commitSync();
}
}
}
问题8、Kafka为什么要分区,生产数据的分区策略
- 采用分区的原因:
1)方便在集群中扩展,每个partition可以调整适应其所在机器,而topic可以由多个partition组成,因此整个集群可以存储任意大小的消息数据。
2)以partition为单位读写,可以提高并发。 - 查看Partition接口的默认实现类DefaultPartitioner,可获知分区规则
1)指定了patition号,则直接使用(直接将数据发送到指定的分区);
2)未指定patition好但指定key,通过对key的value取hashCode来确定分区
3)patition和key都未指定,使用轮询选出一个patition
另外,还可以通过自定义Partition类进行分区。
问题9、Kafka消费者的分区分配策略
- 默认为range范围策略
分区数量除以消费者数量得到n和余数m,前m个消费者分配到n+1个分区,剩下的消费者分配到n个分区 - roundrobin轮询策略
所有消费者构成一个消费者闭环,将分区逐个轮流分配给环上的消费者。
问题10、传统的消息传递方法与kafka的不同
- 传统消息队列一般是发布-订阅模式,如activeMQ ,消息生产者将消息发布到activeMQ服务器,所有订阅该相关主题的消费者将收到通知(广播),这一过程有事务的支持,主要用于业务系统中,消息的吞吐量不是很大。
- Kafka采取的是push-pull模式,即生产者将消息推送到kafka集群的topic,消费者主动地去topic中拉取数据。相对于传统的消息传递方法。
问题11、Kafka的ISR机制
- kafka 使用多副本保证消息不丢失,不是完全同步或者完全异步,而是使用ISR机制
- leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护
- 如果一个follower比一个leader落后太多,或者超过一定时间(500ms)未发起数据复制请求,则leader将其从ISR中移除
- 当ISR中所有Replica都向Leader发送ACK后,leader才commit,这时producer才能认为一个请求中的消息成功提交到kafka中。
- 注:根据配置replica.lag.time.max.ms,多久没同步,就会从ISR列表中剔除。以前还有根据落后多少条消息就踢出ISR,在1.0版本后就去掉了,因为这个值很难取,在高峰的时候很容易出现节点不断的进出ISR列表。
在Zookeeper的/brokers/ids节点上注册Watch。当broker宕机,从zk的/brokers/topics/[topic]/partitions/[partition]/state中,读取对应partition的ISR列表,选一个出来做leader。
Kafka的信息复制确保了任何已发布的消息不会丢失,并且可以在机器错误、程序错误或更常见些的软件升级中使用。
问题12、如何保证Kafka的消息有序
- 一般情况下,Kafka对于消息的重复、丢失及顺序没有严格要求,kafka保证了一个partition中的消息是有序的,但存在多个partition时,不保证全局有序。
- 需要消息有序被消费,有两个可行方案:
1】设置topic只使用一个分区,这样所有消息都存储在同一分区中,是全局有序的。缺陷是消息只能被消费者组中的一个消费者处理,降低了性能,不适用高并发的情况。
2】在producer发送消息时,给需要有序的消息指定分区,将消息发给同一个分区进行存储,这样也可以保证消息有序。Producer发送消息指定分区,可以有两种方式
1)直接给定分区号
2)不给定分区号,指定key,kafka会根据key的hashcode来确定分区。
问题13、Kafka的高吞吐怎么实现的
1】顺序写磁盘
Kafka的消息不断追加到文件中,在磁盘上只做Sequence I/O,顺序读写不需要硬盘磁头的寻道时间,只需很少的扇区旋转时间,比随机读写磁盘(Random I/O)性能高上千倍。
2】使用PageCache功能,并采用了sendFile技术
PageCache把尽可能多的空闲内存都当做磁盘缓存来使用,这样避免在JVM内部缓存数据,JVM的GC机制会频繁扫描Heap空间,带来不必要的开销。
SendFile直接在内核空间完成数据拷贝,跳过用户空间的I/O操作,建立磁盘空间和内核空间缓存的直接映射,即零拷贝系统调用,大大提高性能
3】更高效的存储格式。
一般的JMS需要沉重的消息头,以及维护各种索引结构的开销。Kafka使用简明的offset机制来标识消息的位置,存储数据更高效。
4】文件分区分段
Kafka的topic可以划分多个partition,每个partition的文件有分为多个段(segment),队列中的消息实际保存多个片段文件中,每次对消息的消费都是对一个小文件的操作,非常轻便,增加了并行处理能力。
5】批量发送(异步方式生成消息)
Kafka可以设置批量发送消息,先将消息缓存在内存,等达到指定的数据量或者一定时间间隔进行发送,减少服务端的网络IO。
6】数据压缩
Kafka还支持对消息集合进行压缩,减少传输的数据量,减轻对网络传输的压力。
问题14、Kafka的工作流程分析
-
生产过程
1、写入方式
producer 采用推(push)模式将消息发布到 broker,每条消息都被追加(append)到分
区(patition)中,属于顺序写磁盘(顺序写磁盘效率比随机写内存要高,保障 kafka 高吞吐量)。
2、写入流程:
1】producer从zookeeper的”/brokers/…/state”节点找到消息对应partition的leader,将消息发送给leader
2】leader将消息写入本地log,
3】followers从leader拉取(pull)消息,写入本地log后向leader发送ACK应答
4】leader收到所有ISR中的Relication的ACK后,向producer发送ACK,写入完成。 -
消息保存过程
1、存储方式
topic分为一个或多个partition,每个partition对应一个文件夹,存储该分区的所有.log消息文件和.index索引文件。
2、存储策略
无论消息是否被消费,kafka都会保留所有消息,有两种策略可以删除旧数据:
1】基于时间:log.retention.hours = 168 (一周)
2】基于大小:log.retention.bytes = 1073741824 (1G)
Kafka 读取特定消息的时间复杂度为 O(1),即与文件大小无关, 所以删除过期文件与提高 Kafka 性能无关 -
消费过程
1、消费消息的方式
1】高级API
不需要去自行去管理offset,系统通过zookeeper自行管理;
不需要管理分区,副本等情况,系统自动管理;
消费者断线会自动根据上一次记录在zookeeper中的offset去接着获取数据;
不能自行控制offset,不能细化控制如分区、副本、zk等
2】低级API
能够开发者自己控制offset,自行控制连接分区,对分区自定义进行负载均衡,
对zookeeper的依赖性降低;相对复杂,需要自行控制offset,连接哪个分区,找到分区leader 等
2、消费者组
消费者是以consumer group消费者组的方式工作,由一个或者多个消费者组成一个组,共同消费一个topic。每个分区在同一时间只能由group中的一个消费者读取,但是多个group可以同时消费这个partition。
在这种情况下,消费者可以通过水平扩展的方式同时读取大量的消息。另外,如果一个消费者失败了,那么其他的group成员会自动负载均衡读取之前失败的消费者读取的分区。
3、消费方式
consumer采用pull(拉)模式从broker中读取数据,consumer可自主控制消费消息的速率,同时consumer可以自己控制消费方式——即可批量消费也可逐条消费,同时还能选择不同的提交方式从而实现不同的传输语义。
问题15、Kafka怎么解决数据积压
- 一般来说,生产消息的速度比消费消息的速度快,造成数据积压。
解决方案:
1】增加partitioner数量,从而提高consumer并行消费的能力,注意单纯增加消费者个数,并不能解决问题,反而会有资源浪费或者重复消费的问题。
2】 消费者每次poll的数据业务处理时间不能超过kafka的max.poll.interval.ms,该参数在kafka0.10.2.1中的默认值是300s,要综合业务处理时间和每次poll的数据数量。
注:max.poll.records默认是2147483647 (0.10.0.1版本),也就是kafka里面有多少poll多少,如果消费者拿到的这些数据在制定时间内消费不完,就会手动提交失败,数据就会回滚到kafka中,会发生重复消费的情况。如此循环,数据就会越堆越多。
问题16、Kafka为什么写入性能很高
一、顺序读写
Kafka就是使用了磁盘顺序读写来提升的性能,基于磁盘的随机读写确实很慢,但磁盘的顺序读写性能却很高
二、Page Cache
为了优化读写性能,Kafka利用了操作系统本身的Page Cache,就是利用操作系统自身的内存而不是JVM空间内存,避免了JVM的GC
三、零拷贝
Kafka利用 linux 操作系统的 “零拷贝(zero-copy)” 机制在消费端做的优化
四、分区分段
Kafka的message是按topic分类存储的,topic中的数据又是按照一个一个的partition即分区存储到不同broker节点。每个partition对应了操作系统上的一个文件夹,partition实际上又是按照segment分段存储的。
总结
Kafka采用顺序读写、Page Cache、零拷贝以及分区分段等这些设计,再加上在索引方面做的优化,另外Kafka数据读写也是批量的而不是单条的,使得Kafka具有了高性能、高吞吐、低延时的特点。这样,Kafka提供大容量的磁盘存储也变成了一种优点
问题17、kafka申请topic命令
bin/kafka-topic.sh \
--create //操作类型,如果是修改的话用alter
--zookeeper node01:2181 //kafka依赖的zookeeper集群地址
--replication-factor 2 //副本因子
--partitions 1 //分区数量
--topic test //topic名称