从KafkaConsumer看看Kafka(一)
Kafka的消息模型为发布订阅模型,消息生产者将消息发布到主题(topic)中,一个或多个消费者订阅(消费)该主题消息并消费,此模型中发布到topic中的消息会被所有消费者所订阅到,先介绍Kafka消费模型,然后再通过KafkaConsumer原来了解它的业务流程,源码基于kafka 2.4;
Kafka消费模型关键点:
1、Kafka一个消费组(ConsumerGroup)中存在一个或多个消费者(Consumer),每个消费者也必须属于一个消费者组;
2、消费者组(ConsumerGroup)中的消费者(Consumer)独占一个或多个分区(Partition);
3、消费时每个分区(Partition)最多只有一个Consumer再消费;
4、消费者组(ConsumerGroup)在Broker存在一个协调者(Coordinator)分配管理Consumer与Partition之间的对应关系。当两种中的Consumer或Partition发生变更时将会触发reblance(重新平衡),重新分配Consumer与Partition的对应关系;
下面是Kafka消费者程序的示例:
//配置Consumer
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "test");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
//创建Consumer
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
//订阅主题
consumer.subscribe(Arrays.asList("foo", "bar"));
//消费消息
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());
}
在上面我们可以看到Kafka消费消息的整个流程:配置Consumer属性、订阅主题、拉取消费消息,基本流程知道了也就是这几个点,配置ConsumerId、自动提交offset、序列化、Kafka服务端地址,这就是Kafka最最最基础的配置,当然还有很多配置项可以到官网查看;
消费者关键点
Consumer程序主要分为三个部分:配置、订阅主题、拉取消息;从中也可以看到在消费前需要订阅某个主题、在前面我们提到Consumer实例需要与某个Partition绑定关联然后才能进行消费数据,下面我们透过官方提供的Consumer程序简单看看如何订阅主题、如何关联Consumer与Partition、如何拉取消息消费;
订阅主题
订阅主题可以说是Kafka消费的基础,下面先看看简化后的订阅方法:
public void subscribe(Collection<String> topics, ConsumerRebalanceListener listener) {
acquireAndEnsureOpen();
try {
//忽略部分代码
if (topics.isEmpty()) {
this.unsubscribe();
} else {
if (this.subscriptions.subscribe(new HashSet<>(topics), listener))
metadata.requestUpdateForNewTopics();
}
} finally {
release();
}
}
安全检查: Consumer注释中也说了KafkaConsumer为非线程安全的,从上也可看到acquireAndEnsureOpen的作用就是检查当前是否为多线程运行,确保Consumer只在一个线程中执行;
设置订阅状态: SubscriptionState 对象的subscribe方法主要是设置ConsumerRelance监听器、设置所监听的主题;
更新元数据: metadata对象维护了Kafka集群元数据子集,存储了Broker节点、Topic、Partition节点信息等;
跟进metadata.requestUpdateForNewTopics方法发现最终调用了metadata对象的requestUpdate方法;
public synchronized int requestUpdate() {
this.needUpdate = true;
return this.updateVersion;
}
此方法并没有什么实质性的动作,只是更新needUpdate属性为true;由于Kafka拉取数据时必须得到元数据信息否则无法知道broker、topic、Partition信息也就无法知道去哪个节点拉取数据;但此处并没有实质性的更新元数据请求,接下来我们看看拉取方法。
拉取数据
上一步订阅了主题,这时我们就可以从中拉取数据,跟踪代码最终进入了KafkaConsumer的poll方法;
private ConsumerRecords<K, V> poll(final Timer timer, final boolean includeMetadataInTimeout) {
//多线程检查
acquireAndEnsureOpen();
try {//省略代码
//超时检查
if (includeMetadataInTimeout) {
//请求更新元数据
if (!updateAssignmentMetadataIfNeeded(timer)) {
return ConsumerRecords.empty();
}
} else {//省略代码
}
//拉取数据
final Map<TopicPartition, List<ConsumerRecord<K, V>>> records = pollForFetches(timer);
if (!records.isEmpty()) {
if (fetcher.sendFetches() > 0 || client.hasPendingRequests()) {
client.pollNoWakeup();
}
//调用消费者拦截器后返回
return this.interceptors.onConsume(new ConsumerRecords<>(records));
}
return ConsumerRecords.empty();
} finally {
release();
this.kafkaConsumerMetrics.recordPollEnd(timer.currentTimeMs());
}
}
此方法几个流程
1、 多线程检查
2、 超时检查
3、 请求更新元数据
4、 拉取数据
此处我们比较关心的还是更新元数据与拉取数据,这里我们主要看看这两个流程的执行;
请求更新元数据
在updateAssignmentMetadataIfNeeded方法中调用coordinator对象的poll方法去更新元数据,并且调用updateFetchPositions方法用于刷新Consumer对应Partition对应的offset值;
拉取数据
数据的拉取在pollForFetches方法中;
private Map<TopicPartition, List<ConsumerRecord<K, V>>> pollForFetches(Timer timer) {
//省略代码
//从缓存区数据
final Map<TopicPartition, List<ConsumerRecord<K, V>>> records = fetcher.fetchedRecords();
if (!records.isEmpty()) {
return records;
}
//构造拉取请求发送
fetcher.sendFetches();
//省略代码
//发起拉取数据请求
Timer pollTimer = time.timer(pollTimeout);
client.poll(pollTimer, () -> {
// since a fetch might be completed by the background thread, we need this poll condition
// to ensure that we do not block unnecessarily in poll()
return !fetcher.hasAvailableFetches();
});
timer.update(pollTimer.currentTimeMs());
//省略代码
return fetcher.fetchedRecords();
}
pollForFetches方法执行逻辑:
1、 从缓存取数据如有可用数据,直接返回;
2、 构造请求对象fetches,一个节点node对应一个clientRequest对象,将其放入ConsumerNetworkClient对象的unsent属性中;
3、 调用client对象poll方法,将上一步放入unsent属性的请求对象ClientRequest发送出去;
4、 返回所拉取到的消息;
Offset提交
offset提交放在ConsumerCoordinator对象中,offset提交又分为自动提交与手动提交;当设置了enable.auto.commit==true且 autoCommitIntervalMs等于指定间隔时有这么几个时机会触发自动:
1、 consumer对象close时,调用commitOffsetsSync触发同步的offset提交;
2、 consumer对象poll时,调用commitOffsetsAsync触发异步的offset提交;
3、 触发Partition与Topic 分配 assign时触发commitOffsetsAsync异步提交;
4、 当发生relance或有Consumer加入Group时触发commitOffsetsSync方法同步提交;
参考资料: http://kafka.apache.org
从KafkaConsumer看看Kafka(一)的更多相关文章
- Windbg调优Kafka.Client内存泄露
从来没写过Blog,想想也是,工作十多年了,搞过N多的架构.技术,不与大家分享实在是可惜了.另外,从传统地ERP行业转到互联网,也遇到了很所前所未有的问题,原来知道有一些坑,但是不知道坑太多太深.借着 ...
- Python 使用python-kafka类库开发kafka生产者&消费者&客户端
使用python-kafka类库开发kafka生产者&消费者&客户端 By: 授客 QQ:1033553122 1.测试环境 python 3.4 zookeeper- ...
- Kafka消费者组再均衡问题
在Kafka中,当有新消费者加入或者订阅的topic数发生变化时,会触发Rebalance(再均衡:在同一个消费者组当中,分区的所有权从一个消费者转移到另外一个消费者)机制,Rebalance顾名思义 ...
- KafkaConsumer 长时间地在poll(long )方法中阻塞
一,问题描述 搭建的用来测试的单节点Kafka集群(Zookeeper和Kafka Broker都在同一台Ubuntu上),在命令行下使用: ./bin/kafka-topics. --replica ...
- python连接kafka生产者,消费者脚本
# -*- coding: utf-8 -*- ''''' 使用kafka-Python 1.3.3模块 # pip install kafka==1.3.5 # pip install kafka- ...
- kafka consumer重复消费问题
在做分布式编译的时候,每一个worker都有一个consumer,适用的kafka+zookeep的配置都是默认的配置,在消息比较少的情况下,每一个consumer都能均匀得到互不相同的消息,但是当消 ...
- kafka+docker+python
昨天晚上刚刚才花3小时看完<日志:每个软件工程师都应该知道的有关实时数据的统一概念>. 今天就把kafka在docker容器里运行起来,github上有几个,但都太复杂了. 我自己写个最简 ...
- Kafka 温故(五):Kafka的消费编程模型
Kafka的消费模型分为两种: 1.分区消费模型 2.分组消费模型 一.分区消费模型 二.分组消费模型 Producer : package cn.outofmemory.kafka; import ...
- kafka offset 设置
from kafka import KafkaConsumer from kafka import TopicPartition from kafka.structs import OffsetAnd ...
随机推荐
- 新闻实时分析系统-Hadoop2.X分布式集群部署
(一)hadoop2.x版本下载及安装 Hadoop 版本选择目前主要基于三个厂商(国外)如下所示: 1.基于Apache厂商的最原始的hadoop版本, 所有发行版均基于这个版本进行改进. 2.基于 ...
- 【CPLUSOJ】【USACO】【差分约束】排队(layout)
[题目描述] Robin喜欢将他的奶牛们排成一队.假设他有N头奶牛,编号为1至N.这些奶牛按照编号大小排列,并且由于它们都很想早点吃饭,于是就很可能出现多头奶牛挤在同一位置的情况(也就是说,如果我们认 ...
- linux alias(命令别名)
alias:获取定义的所有命令别名 alias NAME='COMMAND':定义别名 unalias NAME:撤销别名
- 【漏洞复现】Apache Solr远程代码执行(CVE-2019-0193)
0x01 概述 Solr简介 Apache Solr 是一个开源的企业级搜索服务器.Solr 使用 Java 语言开发,主要基于 HTTP 和 Apache Lucene 实现.Apache Solr ...
- java程序员面试宝典之——Java 基础部分(1~10)
基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io 的语法,虚拟机方面的语法. 1.一个".java"源文件中是否可以包 ...
- Linux之find命令
1.find命令的作用 主要用于操作系统文件.目录的查找. 2.find命令常用参数 -name #按文件名查找 -type #按文件类型查找:b/p/c/p/l/f -size #但文件大小查找,G ...
- 这是一个测试 hello world
第一次写博客,冒着生命危险尝试一下,发说说 搞了半天,发现原创文章在随笔发,我以为在文章目录发,白白在文章那里建了分类,太难了吧我
- 转:Java实现敏感词过滤
敏感词.文字过滤是一个网站必不可少的功能,如何设计一个好的.高效的过滤算法是非常有必要的.前段时间我一个朋友(马上毕业,接触编程不久)要我帮他看一个文字过滤的东西,它说检索效率非常慢.我把它程序拿过来 ...
- 在Eclipse中混合Java和Scala编程
1. 新建项目目录 scala-java-mix 2. 创建 src 目录及子目录: mkdir -p src/main/java mkdir -p src/main/scala 3. 在目录 sca ...
- .NETCore 访问国产达梦数据库
前言 武汉达梦数据库有限公司成立于2000年,为中国电子信息产业集团(CEC)旗下基础软件企业,专业从事数据库管理系统的研发.销售与服务,同时可为用户提供大数据平台架构咨询.数据技术方案规划.产品部署 ...