https://www.cnblogs.com/sunrise88/p/7286009.html
1、kafka的数据存在内存还是磁盘
Kafka最核心的思想是使用磁盘,而不是使用内存,可能所有人都会认为,内存的速度一定比磁盘快,我也不例外。在看了Kafka的设计思想,查阅了相应资料再加上自己的测试后,发现磁盘的顺序读写速度和内存持平。
而且Linux对于磁盘的读写优化也比较多,包括read-ahead和write-behind,磁盘缓存等。如果在内存做这些操作的时候,一个是JAVA对象的内存开销很大,另一个是随着堆内存数据的增多,JAVA的GC时间会变得很长,使用磁盘操作有以下几个好处:
磁盘缓存由Linux系统维护,减少了程序员的不少工作。
磁盘顺序读写速度超过内存随机读写。
JVM的GC效率低,内存占用大。使用磁盘可以避免这一问题。
系统冷启动后,磁盘缓存依然可用。
2、spark streaming 读取kafka数据的两种方式(Receiver-base、Direct)
(数据丢失、复制两份资源浪费、数据重复消费、并行度效率)
Receiver
Receiver 是使用 Kafka 的高层次 Consumer API 来实现的。Receiver 每隔一段 batch 时间去 Kafka 获取那段时间最新的消息数据,Receiver 从 Kafka 获取的数据都 是存储在 Spark Executor 的内存中的,然后 Spark Streaming 启动的 job 会去处理那 些数据。
Receiver方式是使用高级API,需要消费者连接zookeeper来读取数据。是由zookeeper来维护偏移量,不用我们手动维护,这样的话就比较简单,减少了代码量。高阶消费者 API 会根据参数配置隔几秒提交一次。高阶消费者是由高阶消费者 API 自己提交 offset 到 ZooKeeper 中。
但是特有很多缺点:
1、丢失数据。他是由Executor内的Receiver来拉取数据并存放到内存中,再由Driver端提交的job(Spark Streaming启动的job)来处理数据。这样的话,如果底层节点出现错误,就会发生数据丢失。
这会引起一个问题,当 Spark Streaming 中的 Receiver 读取 Kafka 分区数据时, 假设读取了 100 条数据,高阶消费者 API 会执行 offset 的提交,例如每隔 3 秒,这 100 条数据就是 RDD,假设此 RDD 还没有处理完,高阶消费者 API 执行了 offset 提交,但是 Spark Streaming 挂掉了,由于 RDD 在内存中,那么 RDD 的数据就丢失 了,如果想重新拿数据,从哪里去拿不是由 Spark Streaming 说了算的,是由高阶 API 决定的,由于 offset 已经提交,高阶 API 认为这个数据 Spark Streaming 已经拿 过了,再拿要拿 100 条以后的数据,那么之前丢失的 100 条数据就永远丢失了。
2、浪费资源。如果要启用高可靠机制,让数据零丢失,就必须启用Spark Streaming的预写日志机制(Write Ahead Log,WAL)。spark采用了WALs方式将数据同步到高可用数据存储(HDFS),即使底层节点出现了失败,也可以使用预写日志中的数据进行恢复。kafka自己本身就有可靠的机制,会对数据复制一份,就导致了一份数据存储两份,浪费了资源。
针对这一问题,Spark Streaming 设计了一个规则,即 Spark Streaming 预写日志 规则(Write Ahead Log,WAL),每读取一批数据,会写一个 WAL 文件,在 WAL 文件中,读了多少条就写多少条,WAL 文件存储于 HDFS 上。假设 RDD 中有 100 条数据,那么 WAL 文件中也有 100 条数据,此时如果 Spark Streaming 挂掉,那么 回去读取 HDFS 上的 WAL 文件,把 WAL 文件中的 100 条数据取出再生成 RDD,然 后再去消费。由于这一设计需要写 HDFS,会对整体性能造成影响。
3、效率低:因为是分批次执行的,他是接受数据,直到达到了设定的时间间隔,才进行计算。而且我们在kafkaUtils.creatStream()中设定的partition数量,只会增加reciver数量,不能提高并行计算的效率,但我们可以设定不同的group和topic创建Dstream,然后再用Union合并Dstream,提高并行度。
假设有 6 个分区,高阶消费者的话会在 Spark 集群的 Worker 上启动 Receiver, 有 6 个分区则会用 6 个线程去读取分区数据,这是在一个 Worker 的一个 Receiver 中有 6 个线程同时读取 6 个分区的数据,随着数据量越来越大,数据读取会成为瓶 颈,此时可以创建多个 Receiver 分散读取分区数据,然后每个 Receiver 创建一个 Dstream,再把这些流全部都合并起来,然后进行计算。读取时,一方面把 RDD 放 在内存中,一方面写 HDFS 中的 WAL 文件。
根据上面的情景,又要创建多个 Receiver,又要进行合并,又要在内存中存 储 RDD,又要写 HDFS 上的 WAL 文件,高级 API 的缺点还是比较多的。
Driect方式
Spark1.3中引入Direct方式,用来替代掉使用Receiver接收数据,这种方式会周期性地查询Kafka,获得每个topic+partition的最新的offset,从而定义每个batch的offset的范围。当处理数据的job启动时,就会使用Kafka的简单consumer api来获取Kafka指定offset范围的数据。
则采用的是最底层的API,直接连接在kafka服务器上读取数据。需要我们自己维护偏移量,代码量稍微大些。
不过这种方式的优点:
1、当我们读取topic下的数据的时候,它会自动对应Topic下的partition生成相应数量的RDD的Partition,将 Kafka 分区与 RDD 分区做一对一映射,提高了计算时的并行度,提高了效率。
2、他不需要通过WAL来维持数据的完整性。不需再维护一份 WAL 数据,提高了性能。采取Driect直连方式时,当数据发生丢失,只要kafka上的数据进行了复制,就可以根据副本来进行数据的重新拉取。
2、他保证了数据只消费一次,因为我们将偏移量保存在一个地方(mysql,zookeeper,hbase),当我们读取数据时,从这里拿到数据的起始偏移量和读取偏移量确定读取范围,通过这些我们可以读取数据,当读取完成后会更新偏移量,这就保证了数据只消费一次。
假设有 5 个分区,第一次 Spark Streaming 读取 100 条数据,那么每个 partition 都会读取 100 条数据,这 100 条数据对应 offset 是 0~99,这 5 个分区的 100 条数据 数据直接对应 RDD 的 5 个分区,针对这一 RDD 会启动一个 job 进行处理,job 启动 时会将 job 信息和 offset(0~99)写入 CheckpointPath,处理完成前保存 job 和 offset, 一旦处理完成,job 信息会被删除,但是 offset 信息会被保留,通过这次的 offset 确 定下一次的读取范围,即 100~199,新的 job 信息会被写入,新的 offset 100~199 覆 盖原来的 0~99。如果处理第二批次的时候挂掉了,offset 还在,就可以重读这块数 据。
2. Kafka 的 producer 工作流程?
- 封装为
ProducerRecord
实例 - 序列化
- 由 partitioner 确定具体分区
- 发送到内存缓冲区
- 由 producer 的一个专属 I/O 线程去取消息,并将其封装到一个批次 ,发送给对应分区的 kafka broker
- leader 将消息写入本地 log
- followers 从 leader pull 消息,写入本地 log 后 leader 发送 ACK
- leader 收到所有 ISR 中的 replica 的 ACK 后,增加 HW(high watermark,最后 commit 的 offset) 并向 producer 发送 ACK
3、Kafka 的 consumer 工作流程?
- 连接 ZK 集群,拿到对应 topic 的 partition 信息和 partition 的 leader 的相关信息
- 连接到对应 leader 对应的 broker
- consumer 将自己保存的 offset 发送给 leader
- leader 根据 offset 等信息定位到 segment(索引文件和日志文件)
- 根据索引文件中的内容,定位到日志文件中该偏移量对应的开始位置读取相应长度的数据并返回给 consumer
5、数据丢失和重复消费的场景?
- producer 端: I/O 线程发送消息之前,producer 崩溃, 则 producer 的内存缓冲区的数据将丢失。
- consumer 端:不是严格意义的丢失,其实只是漏消费了。 设置了
auto.commit.enable=true
,当 consumer fetch 了一些数据但还没有完全处理掉的时候,刚好到 commit interval 触发了提交 offset 操作,接着 consumer 挂掉。这时已经fetch的数据还没有处理完成但已经被commit掉,因此没有机会再次被处理,数据丢失。 - 另外, 在高阶消费者中,offset 采用自动提交的方式, 自动提交时,假设 1s 提交一次 offset 的更新,设当前 offset = 10,当消费者消费了 0.5s 的数据,offset 移动了 15,由于提交间隔为 1s,因此这一 offset 的更新并不会被提交,这时候我们写的消费者挂掉,重启后,消费者会去 ZooKeeper 上获取读取位置,获取到的 offset 仍为10,它就会重复消费. 解决办法使用低级消费者
5、producer 重复数据的场景?
网络抖动导致 producer 误以为发送错误,导致重试,从而产生重复数据,可以通过幂等性配置避免。
6、Kafka 的数据丢失,高可靠性和一致性是怎么实现的?
可以参见我这篇文章:Kafka 是如何保证数据可靠性和一致性
6、producer端、broker 端丢失数据如何解决?
宏观上看保证数据的可靠安全性,肯定是依据分区数做好数据备份,设立副本数。
broker端丢失数据如何解决?
topic设置多分区,分区自适应所在机器,为了让各分区均匀分布在所在的broker中,分区数要大于broker数。
分区是kafka进行并行读写的单位,是提升kafka速度的关键。
【消息ack机制】
ack=0:生产者消息发送后立即返回,不用确认消息是否发送成功。(性能最好,可靠性最差。发过去就完事了,不关心broker是否处理成功,可能丢数据。)
ack=1:(默认)生产者消息发送后,等待leader写入成功返回ack,则生产者才返回。(性能和可靠性相对平衡。当写Leader成功后就返回,其他的replica都是通过fetcher去同步的,所以kafka是异步写,主备切换可能丢数据。)
ack=all或者-1:生产者消息发送后,不仅leader写入成功,还需要其他节点分区写入成功后,生产者才返回。所有follower都响应了才认为消息提交成功(性能最差,可靠性最好。要等到isr里所有机器同步成功,才能返回成功,延时取决于最慢的机器。强一致,不会丢数据。)
acks = -1 的情况下,数据发送到 leader 后 ,部分 ISR 的副本同步,leader 此时挂掉。比如 follower1 和 follower2 都有可能变成新的 leader, producer 端会得到返回异常,producer 端会重新发送数据,数据可能会重复
【同步or异步】
1.kafka的消息发送分为同步(默认,实时的)和异步(达到某种条件发送)。可通过 producer.type 属性进行配置
同步:生产者写一条数据,该数据立刻写入到某个分区。(数据重要,不能丢失,如银行数据)
异步:生产者写一条数据,先写入缓存,然后以batch(批量)的形式再写入到分区。(数据不是很重要,可以丢失一两条,如日志)
注意:如果设置成异步的模式,可以运行生产者以batch的形式push数据,这样会极大的提高broker的性能,但是这样会增加丢失数据的风险。
-
同步发送,性能差,不推荐。
-
仍然异步发送,通过“无消息丢失配置” 极大降低丢失的可能性:
1.同步模式下的设置
(1)模式设置为同步模式,producer.type = sync
(2)ack设置为-1,Request.required.acks = -1
(3)副本数大于等于2,replication.factor >= 2 且 min.insync.replicas >= 2
2.异步发送相关参数:
producer.type = async 默认是sync
当满足以下其中一个条件的时候就触发 发送
batch.num.messages=200 异步发送 每次批量发送的条目
queue.buffering.max.ms=5000 异步发送的时候 发送时间间隔 单位是毫秒
不限制阻塞超时时间。就是一满生产者就阻塞
request.required.acks=1
queue.buffering.max.messages=10000 当使用async模式时,在在producer必须被阻塞或者数据必须丢失之前,可以缓存到队列中的未发送的最大消息条数
queue.enqueue.timeout.ms = -1
4. kafka其他重要参数设置
- buffer.memory: 指定 producer 端用于缓存消息的缓冲区的大小,默认 32M;适当提升该参数值,可以增加一定的吞吐量。producer可以用来缓存数据的内存大小。如果数据产生速度大于向broker发送的速度,producer会阻塞或者抛出异常,以“block.on.buffer.full”来表明。
- block.on.buffer.full = true 尽管该参数在0.9.0.0已经被标记为“deprecated”,所以这里还是显式设置它为true,使得producer将一直等待缓冲区直至其变为可用。否则如果producer生产速度过快耗尽了缓冲区,producer将抛出异常
- max.in.flight.requests.per.connection = 1 限制客户端在单个连接上能够发送的未响应请求的个数。设置此值是1表示kafka broker在响应请求之前client不能再向同一个broker发送请求。注意:设置此参数是为了避免消息乱序
- 使用KafkaProducer.send(record, callback)而不是send(record)方法 自定义回调逻辑处理消息发送失败
- callback逻辑中最好显式关闭producer:close(0) 注意:设置此参数是为了避免消息乱序
- unclean.leader.election.enable=false 关闭unclean leader选举,即不允许非ISR中的副本被选举为leader,以避免数据丢失
- replication.factor >= 3 这个完全是个人建议了,参考了Hadoop及业界通用的三备份原则
- min.insync.replicas > 1 消息至少要被写入到这么多副本才算成功,也是提升数据持久性的一个参数。与acks配合使用
- 保证replication.factor > min.insync.replicas 如果两者相等,当一个副本挂掉了分区也就没法正常工作了。通常设置replication.factor = min.insync.replicas + 1即可
- retries:默认为 0,即不重试,立即失败。一个大于 0 的值,表示重试次数。retries = MAX 无限重试,直到你意识到出现了问题
- batch.size:producer 会将发送分区的多条数据封装在一个 batch 中进行发送,这里的参数指的就是 batch 的大小。该参数值过小的话,会降低吞吐量,过大的话,会带来较大的内存压力。默认为 16K,建议合理增加该值。
7. consumer 端数据丢失、数据重复消费 如何解决?
enable.auto.commit=false 关闭自动提交位移, 在消息被完整处理之后再手动提交位移, 如果为真,consumer所fetch的消息的offset将会自动的同步到zookeeper。
8、kafks消息数据积压,Kafka消费能力不足怎么处理?
1)如果是Kafka消费能力不足,则可以考虑增加Topic的分区数,并且同时提升消费组的消费者数量,消费者数=分区数。(两者缺一不可)
2)如果是下游的数据处理不及时:提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间<生产速度),使处理的数据小于生产的数据,也会造成数据积压。
9、Kafka 分区的目的?
分区对于 Kafka 集群的好处是:实现负载均衡。分区对于消费者来说,可以提高并发度,提高效率。
10、你知道 Kafka 是如何做到消息的有序性?
kafka 中的每个 partition 中的消息在写入时都是有序的,而且单独一个 partition 只能由一个消费者去消费,可以在里面保证消息的顺序性。但是分区之间的消息是不保证有序的。
16、Kafka消息是采用Pull模式,还是Push模式?
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阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发
20、Kafka创建Topic时如何将分区放置到不同的Broker中
- 副本因子不能大于 Broker 的个数;
- 第一个分区(编号为0)的第一个副本放置位置是随机从
brokerList
选择的; - 其他分区的第一个副本放置位置相对于第0个分区依次往后移。也就是如果我们有5个 Broker,5个分区,假设第一个分区放在第四个 Broker 上,那么第二个分区将会放在第五个 Broker 上;第三个分区将会放在第一个 Broker 上;第四个分区将会放在第二个 Broker 上,依次类推;
- 剩余的副本相对于第一个副本放置位置其实是由
nextReplicaShift
决定的,而这个数也是随机产生的;
具体可以参见Kafka创建Topic时如何将分区放置到不同的Broker中。
22、谈一谈 Kafka 的再均衡Rebalance
在Kafka中,当有新消费者加入或者订阅的topic数发生变化时,会触发Rebalance(再均衡:在同一个消费者组当中,分区的所有权从一个消费者转移到另外一个消费者)机制,Rebalance顾名思义就是重新均衡消费者消费。
Rebalance的过程如下:
第一步:所有成员都向coordinator发送请求,请求入组。一旦所有成员都发送了请求,coordinator会从中选择一个consumer担任leader的角色,并把组成员信息以及订阅信息发给leader。
第二步:leader开始分配消费方案,指明具体哪个consumer负责消费哪些topic的哪些partition。一旦完成分配,leader会将这个方案发给coordinator。coordinator接收到分配方案之后会把方案发给各个consumer,这样组内的所有成员就都知道自己应该消费哪些分区了。
所以对于Rebalance来说,Coordinator起着至关重要的作用
rebalance的触发条件有三种:
- 组成员发生变更(新consumer加入组、已有consumer主动离开组或已有consumer崩溃了)
- 订阅主题数发生变更——如果你使用了正则表达式的方式进行订阅,那么新建匹配正则表达式的topic就会触发rebalance
- 订阅主题的分区数发生变更
1、Kafka 如何做到高吞吐、低延迟的呢?
这里提下 Kafka 写数据的大致方式:先写操作系统的页缓存(Page Cache),然后由操作系统自行决定何时刷到磁盘。
Kafka是分布式消息系统,需要处理海量的消息,Kafka的设计是把所有的消息都写入速度低容量大的硬盘,以此来换取更强的存储能力,但实际上,使用硬盘并没有带来过多的性能损失。
- 页缓存是在内存中分配的,所以消息写入的速度很快。
- 顺序读写;追加写的方式,避免了磁盘随机写操作。
- 零拷贝
- 文件分段
- 批量发送
- 数据压缩。
PS: 使用页缓存而非堆内存还有一个好处,就是当 Kafka broker 的进程崩溃时,堆内存的数据会丢失,但是页缓存的数据依然存在,重启 Kafka broker 后可以继续提供服务。
具体参见:Kafka是如何实现高吞吐率的
9、分区策略(即生产消息时如何选择哪个具体的分区)?
- 指定了 key ,相同的 key 会被发送到相同的分区;
- 没有指定 key,通过轮询保证各个分区上的均匀分配。
12、如何选择 Partiton 的数量?
- 在创建 Topic 的时候可以指定 Partiton 数量,也可以在创建完后手动修改。但 Partiton 数量只能增加不能减少。中途增加 Partiton 会导致各个 Partiton 之间数据量的不平等。
- Partition 的数量直接决定了该 Topic 的并发处理能力。但也并不是越多越好。Partition 的数量对消息延迟性会产生影响。
- 一般建议选择 Broker Num * Consumer Num ,这样平均每个 Consumer 会同时读取 Broker 数目个 Partition , 这些 Partition 压力可以平摊到每台 Broker 上。
25、如何为Kafka集群选择合适的Topic/Partitions数量
越多的分区可以提供更高的的吞吐 , 一个话题topic的各个分区partiton之间是并行的。
在producer和broker方面,写不同的分区是完全并行的。因此一些昂贵的操作比如压缩,可以获得更多的资源,因为有多个进程。
在consumer方面,一个分区的数据可以由一个consumer线程在拉去数据。分区多,并行的consumer(同一个消费组)也可以多。因此通常,分区越多吞吐量越高。
基于吞吐量可以获得一个粗略的计算公式。先测量得到在只有一个分区的情况下,Producer的吞吐量(P)和Consumer的吞吐量(C)。那如果总的目标吞吐量是T的话,max(T/P,T/C)就是需要的最小分区数。
在单分区的情况下,Producer的吞吐量可以通过一些配置参数,比如bath的大小、副本的数量、压缩格式、ack类型来测得。而Consumer的吞吐量通常取决于应用程序处理每一天消息逻辑。
这些都是需要切合实际测量。 随着时间推移数据量的增长可能会需要增加分区。有一点需要注意的是,Producer者发布消息通过key取哈希后映射分发到一个指定的分区,当分区数发生变化后,会带来key和分区映射关系发生变化。可能某些应用程序依赖key和分区映射关系,映射关系变化了,程序就需要做相应的调整。为了避免这种key和分区关系带来的应用程序修改。所以在分区的时候尽量提前考虑,未来一年或两年的对分区数据量的要求。 除了吞吐量,还有一些其他的因素,在定分区的数目时是值得考虑的。在某些情况下,太多的分区也可能会产生负面影响。 分区多需要的打开的文件句柄也多 每个分区都映射到broker上的一个目录,每个log片段都会有两个文件(一个是索引文件,另一个是实际的数据文件)。分区越多所需要的文件句柄也就越多,可以通过配置操作系统的参数增加打开文件句柄数。 分区多增加了不可用风险 kafka支持主备复制,具备更高的可用性和持久性。一个分区(partition)可以有多个副本,这些副本保存在不同的broker上。每个分区的副本中都会有一个作为Leader。当一个broker失败时,Leader在这台broker上的分区都会变得不可用,kafka会自动移除Leader,再其他副本中选一个作为新的Leader。Producer和Consumer都只会与Leader相连。 一般情况下,当一个broker被正常关机时,controller主动地将Leader从正在关机的broker上移除。移动一个Leader只需要几毫秒。然当broker出现异常导致关机时,不可用会与分区数成正比。假设一个boker上有2000个分区,每个分区有2个副本,那这样一个boker大约有1000个Leader,当boker异常宕机,会同时有1000个分区变得不可用。假设恢复一个分区需要5ms,1000个分区就要5s。 分区越多,在broker异常宕机的情况,恢复所需时间会越长,不可用风险会增加。 分区多会增加点到点的延迟 这个延迟需要体现在两个boker间主备数据同步。在默认情况下,两个boker只有一个线程负责数据的复制。 根据经验,每个boker上的分区限制在100*b*r内(b指集群内boker的数量,r指副本数量)。 分区多会增加客户端的内存消耗 kafka0.8.2后有个比较好的特色,新的Producer可以允许用户设置一个缓冲区,缓存一定量的数据。当缓冲区数据到达设定量或者到时间,数据会从缓存区删除发往broker。如果分区很多,每个分区都缓存一定量的数据量在缓冲区,很可能会占用大量的内存,甚至超过系统内存。 Consumer也存在同样的问题,会从每个分区拉一批数据回来,分区越多,所需内存也就越大。 根据经验,应该给每个分区分配至少几十KB的内存。 总结 在通常情况下,增加分区可以提供kafka集群的吞吐量。然而,也应该意识到集群的总分区数或是单台服务器上的分区数过多,会增加不可用及延迟的风险。
13、可重试的异常情况有哪些?
- 分区的 leader 副本不可用,一般发生再 leader 换届选举时。
- controller 当前不可用,一般是 controller 在经历新一轮的选举。
- 网络瞬时故障。
14、controller 的职责有哪些?
在 kafka 集群中,某个 broker 会被选举承担特殊的角色,即控制器(controller),用于管理和协调 kafka 集群,具体职责如下:
- 管理副本和分区的状态
- 更新集群元数据信息
- 创建、删除 topic
- 分区重分配
- leader 副本选举
- topic 分区扩展
- broker 加入、退出集群
- 受控关闭
- controller leader 选举
15、leader 挂了会怎样?(leader failover)
当 leader 挂了之后,controller 默认会从 ISR 中选择一个 replica 作为 leader 继续工作,条件是新 leader 必须有挂掉 leader 的所有数据。
如果为了系统的可用性,而容忍降低数据的一致性的话,可以将 unclean.leader.election.enable = true
,开启 kafka 的"脏 leader 选举"。当 ISR 中没有 replica,则会从 OSR 中选择一个 replica 作为 leader 继续响应请求,如此操作提高了 Kafka 的分区容忍度,但是数据一致性降低了。
16、broker 挂了会怎样?(broker failover)
broker上面有很多 partition 和多个 leader 。因此至少需要处理如下内容:
- 更新该 broker 上所有 follower 的状态
- 从新给 leader 在该 broker 上的 partition 选举 leader
- 选举完成后,要更新 partition 的状态,比如谁是 leader 等
kafka 集群启动后,所有的 broker 都会被 controller 监控,一旦有 broker 宕机,ZK 的监听机制会通知到 controller, controller 拿到挂掉 broker 中所有的 partition,以及它上面的存在的 leader,然后从 partition的 ISR 中选择一个 follower 作为 leader,更改 partition 的 follower 和 leader 状态。
17、controller 挂了会怎样?(controller failover)
- 由于每个 broker 都会在 zookeeper 的 "/controller" 节点注册 watcher,当 controller 宕机时 zookeeper 中的临时节点消失
- 所有存活的 broker 收到 fire 的通知,每个 broker 都尝试创建新的 controller path,只有一个竞选成功并当选为 controller。
18、Zookeeper 为 Kafka 做了什么?
- 管理 broker 与 consumer 的动态加入与离开。(Producer 不需要管理,随便一台计算机都可以作为Producer 向 Kakfa Broker 发消息)
- 触发负载均衡,当 broker 或 consumer 加入或离开时会触发负载均衡算法,使得一个 consumer group 内的多个 consumer 的消费负载平衡。(因为一个 comsumer 消费一个或多个partition,一个 partition 只能被一个 consumer 消费)
- 维护消费关系及每个 partition 的消费信息。
19、Page Cache 带来的好处。
Linux 总会把系统中还没被应用使用的内存挪来给 Page Cache,在命令行输入free,或者 cat /proc/meminfo
,“Cached”的部分就是 Page Cache。
Page Cache 中每个文件是一棵 Radix 树(又称 PAT 位树, 一种多叉搜索树),节点由 4k 大小的 Page 组成,可以通过文件的偏移量(如 0x1110001)快速定位到某个Page。
当写操作发生时,它只是将数据写入 Page Cache 中,并将该页置上 dirty 标志。
当读操作发生时,它会首先在 Page Cache 中查找,如果有就直接返回,没有的话就会从磁盘读取文件写入 Page Cache 再读取。
可见,只要生产者与消费者的速度相差不大,消费者会直接读取之前生产者写入Page Cache的数据,大家在内存里完成接力,根本没有磁盘访问。
而比起在内存中维护一份消息数据的传统做法,这既不会重复浪费一倍的内存,Page Cache 又不需要 GC (可以放心使用60G内存了),而且即使 Kafka 重启了,Page Cache 还依然在。
30、Kafka 监控都有哪些?
参见我另外几篇文章:Apache Kafka监控之KafkaOffsetMonitor
雅虎开源的Kafka集群管理器(Kafka Manager)
Apache Kafka监控之Kafka Web Console
还有 JMX