Kafka结构框架自解(三)

三、Kafka的消费者

在消费者模式中,总体存在两种基本的消费模式:一种是push(推)一种是pull(拉取),Kafka对应的消费者不同,所以消费者需要的传输速率要求是不同的,所以这样采用pull的方式最为合适,需要多少数据,消费者自己从kafka中拉取多少数据。

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阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发送)。

pull的模式也存在其缺点,消费者不知道什么时候Kafka有数据,有多少数据,所以如果 kafka 没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,Kafka 的消费者在消费数据时会传入一个时长参数 timeout,如果当前没有
数据可供消费,consumer 会等待一段时间之后再返回,这段时长即为 timeout。

Kafka分区策略

Kafka中最为重要的就是分区的策略问题,kafka中消费数据是以消费者组做为划分的,每个组直接相互独立,并且可以重复消费Kafka中同一份数据,但是每个消费者组中则是瓜分这一份数据,并且带有自己消费数据标志-offset,不能再从头开始消费这组数据,除非换消费者组。

同一个消费者组中消费者的数量也是有没有限制的,但是当你消费者的数量超过topic中partittion数目时,超过的消费者将不会有数据消费到,这造成了资源浪费,所以一般设置消费者数目等于partittion数,这样一一对应,有最大的读取数据并行度,达到最大的工作效率。但是,消费者组中消费者数目小于partition时,就是Kafka分区策略来分配消费的partition数目了。

RoundRobin

在这里插入图片描述

Kafka的分区策略一是:RoundRobin(轮询),这种方式将要消费的topic和partition,经过TopicAndPartition类,经过hash得到各个分区的hash值,consumer1和consumer2一轮询的方式,轮流对应消费的topic。例如:hash之后得到的组是这样的A2,A3,B3,B2,B1,A1;假设先分给consumer1,那么consumer1将会对应分区A2,B3和B1;consumer2将会对应剩余3个。
这种方式将会把topic看着一个整体,来进行对分区进行平均分到消费者那里。

Range

在这里插入图片描述

Kafka第二种分区策略:Range分区策略,这个策略是以topic为单位进行划分的,并不以整体作为划分的方式,将每个topic中partition除以消费者数,若不能整除则不平均分配,将相邻的partition编号给消费者。

存在即合理

为什么存在RoundRobin又存在Range呢,这两种方法都有其缺陷,首先RoundRobin存在的问题
kafka-RoundRobin问题
当消费者组中,组1代码中指出需要消费topicA,组2则需要消费topicB,这时候使用RoundRobin,会将A和B的partition看作一个整体,那么很有可能组1和组2拿到了不应该拿的数据;使用RoundRobin必须保证消费者组中,消费者需要消费的topic保持一致。(所以不是默认策略)
Range的问题则如下图所示,
在这里插入图片描述
由于Range是以topic为组进行partittion的划分,所以当topic数据不断增多时,消费者的消费数据偏移量将会越大,影响消费的速度与数据均衡性。
触发条件:每次消费者组中的消费者数据变化时,会触发分区策略进行数据源的重分配。

offset

因为数据在 Kafka 中是持久化的(7天),故不用担心数据丢失问题。但是当费过程中可能会出现断电宕机等故障时,消费者组消费到哪个数据, consumer 需要实时记录自己消费到了哪个 offset,以便故障恢复后继续消费。
offset 的维护是 Consumer 消费数据是必须考虑的问题。Kafka 0.9 版本之前,consumer 默认将 offset 保存在 Zookeeper 中,从 0.9 版本开始,consumer 默认将 offset 保存在 Kafka 一个内置的 topic 中,该 topic __consumer_offsets。
__consumer_offsets这topic有50个分区,默认建立。
读取 offset

bin/kafka-console-consumer.sh  --topic  __consumer_offsets  --
zookeeper  hadoop102:2181  --formatter
"kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageForm
atter"  --consumer.config  config/consumer.properties  --from-
beginning

offset代码中的自动开启:
enable.auto.commit :是否开启自动提交 offset 功能
auto.commit.interval.ms :自动提交 offset 的时间间隔

Properties props = new Properties();
props.put("bootstrap.servers", "hadoop102:9092");
props.put("group.id", "test");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");

虽然自动提交 offset 十分简介便利,但由于其是基于时间提交的,开发人员难以把握offset 提交的时机。因此 Kafka 还提供了手动提交 offset 的 API。
手动提交 offset 的方法有两种:分别是 commitSync(同步提交)和 commitAsync(异步提交)。两者的相同点是,都会将次 本次 poll 的一批数据最高的偏移量提交;不同点是,commitSync 阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而 commitAsync 则没有失败重试机制,故有可能提交失败。
虽然同步提交 offset 更可靠一些,但是由于其会阻塞当前线程,直到提交成功。因此吞吐量会收到很大的影响。因此更多的情况下,会选用异步提交 offset 的方式。

package com.atguigu.kafka.consumer;
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
public class CustomConsumer {
public static void main(String[] args) {
Properties props = new Properties();
//Kafka 集群
props.put("bootstrap.servers", "hadoop102:9092");
//消费者组,只要 group.id 相同,就属于同一个消费者组
props.put("group.id", "test");
//关闭自动提交 offset
props.put("enable.auto.commit", "false");
props.put("key.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String,  String>  consumer  =  new
KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("first"));//消费者订阅主题
while (true) {
ConsumerRecords<String,  String>  records  =
consumer.poll(100);//消费者拉取数据
for (ConsumerRecord<String, String> record : records) {
System.out.printf("offset = %d, key = %s, value
= %s%n", record.offset(), record.key(), record.value());
}
//异步提交
consumer.commitAsync(new OffsetCommitCallback() {
@Override
public  void  onComplete(Map<TopicPartition,
OffsetAndMetadata> offsets, Exception exception) {
if (exception != null) {
System.err.println("Commit  failed  for"  +
offsets);
}
}
});
}
}
}

offset的维护方面,当新加入partition时,需要考虑消费者的Rebalance‘。
当有新的消费者加入消费者组、已有的消费者推出消费者组或者所订阅的主题的分区发生变化,就会触发到分区的重新分配,重新分配的过程叫做 Rebalance。
消费者发生 Rebalance 之后,每个消费者消费的分区就会发生变化。因此消费者要首先获取到自己被重新分配到的分区,并且定位到每个分区最近提交的 offset 位置继续消费。

参考:尚硅谷kafka课程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值