Kafka基础知识
文章目录
- Kafka基础知识
- 1、Kafka是什么?
- 2、partition的数据文件(offffset,MessageSize,data)
- 3、数据文件分段 segment(顺序读写、分段命令、二分查找)
- 4、负载均衡(partition会均衡分布到不同broker上)
- 5、批量发送
- 6、压缩(GZIP或Snappy)
- 7、消费者设计
- 8、Consumer Group
- 9、如何获取topic主题的列表
- 10、生产者和消费者的命令行是什么?
- 11、consumer是推还是拉?
- 12、讲讲kafka维护消费状态跟踪的方法
- 13、讲一下主从同步
- 14、为什么需要消息系统,mysql 不能满足需求吗?
- 15、Zookeeper对于Kafka的作用是什么?
- 16、Kafka判断一个节点是否还活着有那两个条件?
- 17、Kafka与传统MQ消息系统之间有三个关键区别
- 18、讲一讲kafka的ack的三种机制
- 19、消费者如何不自动提交偏移量,由应用提交?
- 20、消费者故障,出现活锁问题如何解决?
- 21、如何控制消费的位置?
- 22、kafka分布式(不是单机)的情况下,如何保证消息的顺序消费?
- 23、kafka的高可用机制是什么?
- 24、kafka如何减少数据丢失?
- 25、kafka如何不消费重复数据?比如扣款,我们不能重复的扣?
- 参考文档
1、Kafka是什么?
Kafka是一种高吞吐量、分布式、基于发布/订阅的消息系统,最初由LinkedIn公司开发,使用Scala语言编写,目前是Apache的开源项目。
broker: Kafka服务器,负责消息存储和转发
topic:消息类别,Kafka按照topic来分类消息
partition: topic的分区,一个topic可以包含多个partition, topic 消息保存在各个partition上4. offset:消息在日志中的位置,可以理解是消息在partition上的偏移量,也是代表该消息的唯一序号
Producer:消息生产者
Consumer:消息消费者
Consumer Group:消费者分组,每个Consumer必须属于一个group
Zookeeper:保存着集群 broker、 topic、 partition等meta 数据;另外,还负责broker故障发现, partition leader选举,负载均衡等功能
2、partition的数据文件(offffset,MessageSize,data)
答:
partition中的每条Message包含了以下三个属性: offset,MessageSize,data,其中offset表示Message在这个partition中的偏移量,offset不是该Message在partition数据文件中的实际存储位置,而是逻辑上一个值,它唯一确定了partition中的一条Message,可以认为offset是partition中Message的 id; MessageSize表示消息内容data的大小;data为Message的具体内容。
3、数据文件分段 segment(顺序读写、分段命令、二分查找)
答:
Kafka为每个分段后的数据文件建立了索引文件,文件名与数据文件的名字是一样的,只是文件扩展名为index。 index文件中并没有为数据文件中的每条Message建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。
4、负载均衡(partition会均衡分布到不同broker上)
由于消息topic由多个partition组成,且partition会均衡分布到不同broker上。因此,为了有效利用broker集群的性能,提高消息的吞吐量,producer可以通过随机或者hash等方式,将消息平均发送到多个partition上,以实现负载均衡。
![]
5、批量发送
是提高消息吞吐量重要的方式, Producer端可以在内存中合并多条消息后,以一次请求的方式发送了批量的消息给broker,从而大大减少broker存储消息的IO操作次数。但也一定程度上影响了消息的实时性,相当于以时延代价,换取更好的吞吐量。
6、压缩(GZIP或Snappy)
Producer端可以通过GZIP或Snappy格式对消息集合进行压缩。 Producer端进行压缩之后,在Consumer端需进行解压。压缩的好处就是减少传输的数据量,减轻对网络传输的压力,在对大数据处理上,瓶颈往往体现在网络上而不是CPU(压缩和解压会耗掉部分CPU资源)。
7、消费者设计
8、Consumer Group
同一Consumer Group中的多个Consumer实例,不同时消费同一个partition,等效于队列模式。 partition内消息是有序的, Consumer通过pull方式消费消息。 Kafka不删除已消费的消息对于partition,顺序读写磁盘数据,以时间复杂度O(1)方式提供消息持久化能力。
9、如何获取topic主题的列表
bin/kafka-topics.sh --list --zookeeper localhost:2181
新建topic
bin/kafka-topics.sh --create --topic topicAcTrans --partitions 20 --replication-factor 2 --zookeeper xxx:2181
10、生产者和消费者的命令行是什么?
生产者在主题上发布消息:
bin/kafka-console-producer.sh --broker-list 192.168.43.49:9092 --topic Hello-Kafa
注意这里的 IP 是 server.properties 中的 listeners 的配置。接下来每个新行就是输入一条新消息。
消费者接受消息:
bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic Hello-Kafka --from-beginning
或
bin/kafka-console-consumer.sh --bootstrap-server xxx:9092 --from-beginning --topic topicMaTrans
11、consumer是推还是拉?
Kafka最初考虑的问题是,customer应该从brokes拉取消息还是brokers将消息推送到consumer,也就是pull还push。在这方面,Kafka遵循了一种大部分消息系统共同的传统的设计:producer将消息推送到broker,consumer从broker拉取消息。
一些消息系统比如Scribe和Apache Flume采用了push模式,将消息推送到下游的consumer。这样做有好处也有坏处:由 broker决定消息推送的速率,对于不同消费速率的consumer就不太好处理了。消息系统都致力于让consumer以最大的速率最快速的消费消息,但不幸的是,push模式下,当 broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。最终Kafka还是选取了传统的pull模式。
Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker 拉取数据。Push模式必须在不知道下游consumer消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免consumer崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull模式下,consumer就可以根据自己的消费能力去决定这些策略。
Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到t达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发送)。
12、讲讲kafka维护消费状态跟踪的方法
大部分消息系统在broker端的维护消息被消费的记录:
一个消息被分发到consumer后broker就马上进行标记或者等待customer的通知后进行标记。这样也可以在消息在消费后立马就删除以减少空间占用。
但是这样会不会有什么问题呢?如果一条消息发送出去之后就立即被标记为消费过的,一旦consumer处理消息时失败了(比如程序崩溃)消息就丢失了。
为了解决这个问题,很多消息系统提供了另外一个个功能:当消息被发送出去之后仅仅被标记为已发送状态,当接到consumer已经消费成功的通知后才标记为已被消费的状态。
这虽然解决了消息丢失的问题,但产生了新问题,首先如果consumer处理消息成功了但是向broker发送响应时失败了,这条消息将被消费两次。第二个问题时,broker必须维护每条消息的状态,并且每次都要先锁住消息然后更改状态然后释放锁。这样麻烦又来了,且不说要维护大量的状态数据,比如如果消息发送出去但没有收到消费成功的通知,这条消息将一直处于被锁定的状态,Kafka采用了不同的策略。Topic被分成了若干分区,每个分区在同一时间只被一个consumer消费。
这意味着每个分区被消费的消息在日志中的位置仅仅是一个简单的整数:offset。
这样就很容易标记每个分区消费状态就很容易了,仅仅需要一个整数而已。这样消费状态的跟踪就很简单了。这带来了另外一个好处:consumer可以把offset调成一个较老的值,去重新消费老的消息。这对传统的消息系统来说看起来有些不可思议,但确实是非常有用的,谁规定了一条消息只能被消费一次呢?
13、讲一下主从同步
Kafka每个topic的partition有N个副本,其中N是topic的复制因子。Kafka通过多副本机制实现故障自动转移,当Kafka集群中一个Broker失效情况下仍然保证服务可用。在Kafka中发生复制时确保partition的预写式日志有序地写到其他节点上。N个replicas中。其中一个replica为leader,其他都为follower,leader处理partition的所有读写请求,与此同时,follower会被动定期地去复制leader上的数据。
Kafka提供了数据复制算法保证,如果leader发生故障或挂掉,一个新leader被选举并被接受客户端的消息成功写入。Kafka确保从同步副本列表中选举一个副本为leader,或者说follower追赶leader数据。leader负责维护和跟踪ISR(In-Sync Replicas的缩写,表示副本同步队列,具体可参考下节)中所有follower滞后的状态。当producer发送一条消息到broker后,leader写入消息并复制到所有follower。消息提交之后才被成功复制到所有的同步副本。消息复制延迟受最慢的follower限制,重要的是快速检测慢副本,如果follower“落后”太多或者失效,leader将会把它从ISR中删除。
副本同步队列(ISR)
所谓同步,必须满足如下两个条件:
1、副本节点必须能与zookeeper保持会话(心跳机制)
2、 副本能复制leader上的所有写操作,并且不能落后太多。(卡住或滞后的副本控制是由 replica.lag.time.max.ms 配置)
默认情况下Kafka对应的topic的replica数量为1,即每个partition都有一个唯一的leader,为了确保消息的可靠性,通常应用中将其值(由broker的参数offsets.topic.replication.factor指定)大小设置为大于1,比如3。 所有的副本(replicas)统称为Assigned Replicas,即AR。ISR是AR中的一个子集,由leader维护ISR列表,follower从leader同步数据有一些延迟。任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。
HW俗称高水位,是HighWatermark的缩写,取一个partition对应的ISR中最小的LEO作为HW,consumer最多只能消费到HW所在的位置。另外每个replica都有HW,leader和follower各自负责更新自己的HW的状态。对于leader新写入的消息,consumer不能立刻消费,leader会等待该消息被所有ISR中的replicas同步后更新HW,此时消息才能被consumer消费。这样就保证了如果leader所在的broker失效,该消息仍然可以从新选举的leader中获取。对于来自内部broKer的读取请求,没有HW的限制。
- 生产者写入消息,leader更新LEO
- leader通知follwer来取消息,follwer开始fetch数据
- follwer同步的速度是不一样的,有的快,有的慢,以所以follwer最小的offset为准,leader会更新HW值
- 所有的follwer都跟上了leader,leader会再次更新HW
- leader负责读写,follwer阻塞以等待新的消息
下图详细的说明了当producer生产消息至broker后,ISR以及HW和LEO的流转过程:
follwer跟不上节奏怎么办?
如果follower“落后”太多或者失效,leader将会把它从ISR中删除。
14、为什么需要消息系统,mysql 不能满足需求吗?
1.解耦:
允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
2.冗余:
消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的”插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
3.扩展性:
因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
4.灵活性 & 峰值处理能力:
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
5.可恢复性:
系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
6.顺序保证:
在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(Kafka 保证一个 Partition 内的消息的有序性)
7.缓冲:
有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
8.异步通信:
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
15、Zookeeper对于Kafka的作用是什么?
答:
Zookeeper是一个开放源码的、高性能的协调服务,它用于Kafka的分布式应用。Zookeeper主要用于在集群中不同节点之间进行通信在Kafka中,它被用于提交偏移量,因此如果节点在任何情况下都失败了,它都可以从之前提交的偏移量中获取除此之外,它还执行其他活动,如:leader检测、分布式同步、配置管理、识别新节点何时离开或连接、集群、节点实时状态等等。
16、Kafka判断一个节点是否还活着有那两个条件?
答:
(1)节点必须可以维护和ZooKeeper 的连接,Zookeeper通过心跳机制检查每个节点的连接
(2)如果节点是个follower,他必须能及时的同步leader的写操作,延时不能太久
17、Kafka与传统MQ消息系统之间有三个关键区别
答:
(1).Kafka持久化日志,这些日志可以被重复读取和无限期保留
(2).Kafka是一个分布式系统:它以集群的方式运行,可以灵活伸缩,在内部通过复制数据提升容错能力和高可用性
(3).Kafka支持实时的流式处理
18、讲一讲kafka的ack的三种机制
答:
request.required.acks有三个值0 1 -1(all)
0:生产者不会等待broker的ack,这个延迟最低但是存储的保证最弱当server挂掉的时候就会丢数据。
1:服务端会等待ack值leader副本确认接收到消息后发送ack但是如果leader挂掉后他不确保是否复制完成新leader也会导致数据丢失。
-1(all):服务端会等所有的follower的副本受到数据后才会受到leader发出的ack,这样数据不会丢失。
19、消费者如何不自动提交偏移量,由应用提交?
将auto.commit.offset设为false,然后在处理一批消息后commitSync() 或者异步提交commitAsync()
20、消费者故障,出现活锁问题如何解决?
答:
出现 “活锁 ” 的情况, 是它持续的发送心跳, 但是没有处理 。为了预防消费者在这种情况 下一 直持有分区,我们使用max.poll.interval.ms活跃检测 机制 。 在此基础上,如果你调用的poll的频率大于最大间隔,则客户端将主动地离开组,以便其 他消费者接管该分区。发生这种情况时,你会看到offset提交失败(调用commitSync()引发的CommitFailedException)。 这是一种安全机制,保障只有活动成员能够提交offset。所以要留在组中,你必须持续调用poll。
消费者提供两个配置设置来控制poll循环:
max.poll.interval.ms:增大poll的间隔,可以为消费者提供更多的时间去处理返回的消息(调用poll(long)返回的消 息,通常返回的消息都是一 批)。缺点是此值越大将会延迟组重新平衡。
max.poll.records:此设置限制每次调用poll返回的消息数,这样可以更容易的预测每次poll间隔要处理的最大值。通过调整此值,可以减少poll间隔,减少重新平衡分组的对于消息处理时间不可预测地的情况,这些选项是不够的。
处理这种情况的推荐方法是将消息处理移到另一个线程中,让消费者继续调用poll。 但是必须注意确保已提交的offset不超过实际位置。另外,你必须禁用自动提交,并只有在线程完成处理后才为记录手动提交偏移量(取决于你 )。还要注意 ,你需要pause暂停分区,不会从poll接收到新消息,让线程处理完之前返回的消息(如果你的处理能力比拉取消息的慢,那创建新线 程将导致你机器内存溢出) 。
21、如何控制消费的位置?
答:
kafka 使用 seek(TopicPartition, long)指定新的消费位置。用于查找服务器保留的最早和最新的offset的特殊的方法也可用(seekToBeginning(Collection) 和seekToEnd(Collection))
22、kafka分布式(不是单机)的情况下,如何保证消息的顺序消费?
答:
Kafka分布式的单位是partition,同一个partition用一个write ahead log组织,所以可以保证FIFO的顺序。不同partition之间不能保证顺序。但是绝大多数用户都可以通过message key来定义,因为同一个key的Message可以保证只发送到同一个partition。
Kafka中发送1条消息的时候,可以指定(topic, partition, key) 3个参数。partiton和key是可选的。如果你指定了partition,那就是所有消息发往同1个partition,就是有序的。
并且在消费端,Kafka保证,1个partition只能被1个consumer消费。或者你指定key(比如order id),具有同1个key的所有消息,会发往同1个partition。
23、kafka的高可用机制是什么?
答:
这个问题比较系统,回答出kafka的系统特点,leader和follower的关系,消息读写的顺序即可。
24、kafka如何减少数据丢失?
如果要想保证Kafka数据不丢, 要从Kafka的三个地方入手:生产者、服务端和消费者
生产者
在生产中Kafka生产者的开发我们都会用异步调用的方式,异步调用方式有如下两个API:
1)producer.send(msg) 不带回调方法
2)producer.send(msg,callback) 带回调方法
记得要使用带有回调方法的API,我们可以根据回调函数得知消息是否发送成功,如果发送失败了我们要进行异常处理,比如存储到其他介质来保证消息不丢。
acks参数设置:
acks这个参数有三个值:0,1,-1,但是不同的参数对应的含义不同,那如果我们想要保证数据不丢,acks值应该设置为哪个参数呢?请看下面的表格:
0 | 代表生产者只要把消息发送出去以后就认为消息发送成功了,这种方式有可能会导致数据丢失,因为有可能消息发送到服务端以后服务端存储失败了。 |
---|---|
1 | 代表生产者把消息发送到服务端,服务端的leader replica 副本写成功以后,就返回生产者消息发送成功了,这种方式也有可能会导致丢数据,因为有可能刚好数据写入到leader replica,然后返回处理成功的响应给生产者,假如这个时候leader replica在的服务器出问题了,follower replica还没来得及同步数据,这个时候是会丢数据的。 |
-1(all) | 代表生产者把消息发送到服务端,服务端的ISR列表里所有replica 都写入成功以后,才会返回成功响应给生产者。假设ISR列表里面有该分区的三个replica(一个leader replica,两个follower replica),那么acks=-1就意味着消息要写入到leader replica,并且两个follower replica从leader replica上同步数据成功,服务端才会给生产者发送消息发送成功的响应。 所以ISR列表里面的replica就非常关键。如果我们想要保证数据不丢,那么acks的值设置为-1,并且还需要保证ISR列表里面是1个副本以上,具体由哪个参数控制,看下面的服务端的配置。 |
所以acks的值要设置为-1(all)
重试次数设置
为了保证数据不丢,我们尽可能的设置较大的重试次数(参数是retries),如果重试失败了,对异常进行处理,可以把消息保存到另外安全到地方。
服务端
unclean.leader.election.enable
这个参数是控制leader replica出问题了以后follower replica竞选leader replica资格的,我们把设置为false,意思就是如果follower replica如果落后leader replica太多就不能参与竞选。
replication.factor
这个参数设置的是partition副本的个数,如果我们要想保证数据不丢,这个副本数需要设置成大于1。
min.insync.replicas
这个参数要跟生产者里的acks参数配合使用,当生产者acks=-1时,服务端的ISR列表里的所有副本都写入成功,才会给生产者返回成功的响应。而min.insync.replicas这个参数就是控制ISR列表的,假设min.insync.replicas=1,这就意味着ISR列表里可以只有一个副本,这个副本就是leader replica,这个时候即使acks设置的是-1,但其实消息只发送到leader replica,以后就返回成功的响应了。
因为ISR只有一个副本,我们知道这种情况是有可能会丢数据的,所以min.insync.replicas这个值需要大于1的(如果ISR列表里面副本的个数小于min.insync.replicas,生产者发送消息是失败的),并且是min.insync.replicas <= replication.factor
消费者
消费者是可以自动提交offset的,但是如果是自动提交offset,可能会丢数据,比如消费者每隔3秒提交一次offset,假如偏移量成功提交了,但是数据处理失败了,这个时候就会丢数据。所以把enable.auto.commit设置成false就行。
25、kafka如何不消费重复数据?比如扣款,我们不能重复的扣?
答:
其实还是得结合业务来思考,我这里给几个思路:
比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update一下好吧。
比如你是写Redis,那没问题了,反正每次都是set,天然幂等性。
比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的id,类似订单 id之类的东西,然后你这里消费到了之后,先根据这个id去比如Redis里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个id写Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。