KafkaConsumer0.9(三)
来源:互联网 发布:深圳淘宝拍照 编辑:程序博客网 时间:2024/06/14 05:17
Java代码
Properties props =new Properties();
props.put("bootstrap.servers","localhost:9092");
props.put("group.id","test_group");
props.put("enable.auto.commit","true");
props.put("auto.commit.interval.ms","1000");
props.put("session.timeout.ms","30000");
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<String, String>(props);
List<TopicPartition>list = new ArrayList<TopicPartition>();
TopicPartition tp =new TopicPartition("test_topic", 0);
list.add(tp);
consumer.assign(list);
consumer.seek(tp,96);
//consumer.seekToBeginning(tp);
//consumer.seekToEnd(tp);
int commitInterval =200;
List<ConsumerRecord<String,String>> buffer = new ArrayList<ConsumerRecord<String,String>>();
while (true) {
ConsumerRecords<String,String> records = consumer.poll(100);
for(ConsumerRecord<String, String> record : records) {
buffer.add(record);
if(buffer.size() >= commitInterval) {
batchProcessRecords(buffer);
consumer.commitSync();
buffer.clear();
}
}
}
1. 在上一篇中我们看了一个简单的自动提交offset的example,但很多情况下我们为了避免消息丢失,需要确保消息被处理完了之后才提交offset,这就需要手动地提交。在上面这个例子中,我们从kafka中抓取数据并缓存在List中,只有当消息达到一定的数量的时候我们才批量处理,假设我们使用自动提交,如果在我们还没来得及处理之前consumer就异常终止,那么有可能这些消息的offset已经被自动提交掉了,等我们的consumer重新连接上来了之后,上次没有处理完成的消息会被我们完全略过,造成数据丢失,这就是"at-most-once delivery"。解决的办法是,只有在批量处理完消息之后,才用consumer.commitSync()手动地提交offset,但这样的副作用的,假如我们正在批量处理消息,这时consumer异常终止,offset没有被提交但有部分消息已经被处理过了,当consumer重连上来时,这批没有被commit的消息会被重新处理一次,造成会有部分消息被重复处理,这就是"at-least-once delivery"。
2. kafka提供loadbalance机制来确保consumer正常工作,简单的说partitions会被分配给正在监听这个topic的多个consumers(同一个group),当其中一个consumerprocess异常终止,它之前所占有的partitions会被分配给其他consumerprocess,从而保证所有的数据都能被正常消费掉。但有时我们并不需要load balance机制,例如:
- 为了节省网络带宽,我们只希望consumer从某一个partition抓取数据,并存储在本地。这在大数据计算或存储中是很常见的行为。在这种情况下我们并不希望另一台机器的consumer来消费这台机器的partition。
- 如果程序本身带有HA机制,例如使用类似于YARN,Mesos等集群管理框架,那么当一个consumer终止了之后,它会被重启,或者是另一个consumer会被启动来替代它,在这种情况下我们不需要kafka重新分配partition。
要做到这点很简单,替换掉上个例子的consumer.subscribe(Arrays.asList("test_topic")),我们使用consumer.assign(list),在本例中,consumer只会消费partition0的数据。
3. 在之前的版本中,如果我们需要消费旧数据(已经commit offset),我们需要用SimpleConsumer。但是在0.9中,这变得更简单了。在本例中,consumer.seek(tp, 96)表示我们从partition 0的offset 96开始抓取数据,consumer.seekToBeginning(tp)表示从头开始抓取数据,consumer.seekToEnd(tp)表示从最后开始抓取数据,换句话说,只消费consumer启动之后新进来的数据。
kafka 提供三种语义的传递:
1至少一次
2至多一次
3精确一次
首先在 producer端保证1和2的语义是非常简单的,至少一次只需要同步确认即可(确认方式分为只需要 leader确认以及所有副本都确认,第二种更加具有容错性),至多一次最简单只需要异步不断的发送即可,效率也比较高。目前在 producer端还不能保证精确一次,在未来有可能实现,实现方式如下:在同步确认的基础上为每一条消息加一个主键,如果发现主键曾经接受过,则丢弃。
在 consumer端,大家都知道可以控制 offset,所以可以控制消费,其实offset 只有在重启的时候才会用到。在机器正常运行时我们用的是 position,我们实时消费的位置也是 position而不是 offset。我们可以得到每一条消息的 position。如果我们在处理消息之前就将当前消息的 position 保存到 zk上即 offset,这就是至多一次消费,因为我们可能保存成功后,消息还没有消费机器就挂了,当机器再打开时此消息就丢失了;或者我们可以先消费消息然后保存 position 到 zk 上即 offset,此时我们就是至少一次,因为我们可能在消费完消息后offset没有保存成功。而精确一次的做法就是让 position的保存和消息的消费成为原子性操作,比如将消息和 position 同时保存到 hdfs 上 ,此时保存的position 就称为 offset,当机器重启后,从 hdfs重新读入offset,这就是精确一次。
偏移offset
上一段说了分区,分区就是一个有序的,不可变的消息队列.新来的commit log持续往后面加数据.这些消息被分配了一个下标(或者偏移),就是offset,用来定位这一条消息.
消费者消费到了哪条消息,是保持在消费者这一端的.消息者也可以控制,消费者可以在本地保存最后消息的offset,并间歇性的向zookeeper注册offset.也可以重置offset
- KafkaConsumer0.9(三)
- 9解析函数(三)
- (三)
- (三)
- 三消游戏(三)
- 【Android基础】(9)UI控件(三)
- JSP 9大内置对象(三)
- IT之禅(三三)
- 三 序列式容器(三)deque
- 第九周项目三(三)
- 第十五周项目三:OJ(三)
- 三、runtime之消息(三)
- Hibernate(三):三种实例状态
- 三栏布局(三列布局)
- java三大特性:(三)多态
- TCP/IP (三)
- Prefer C++(三)
- 闲谈BCB(三)
- 腾讯亿级排行榜系统实践及挑战
- 一份接地气的数据方案!——能源化工生产管理数据分析
- 寻找最大数
- 机器学习知识点(二十八)Beta分布和Dirichlet分布理解
- Ubuntu Docker 安装
- KafkaConsumer0.9(三)
- 从源码的角度来说说 AsyncTask的问题
- intent 传递数据
- 数据挖掘
- 动态sql语句基本语法--Exec与Exec sp_executesql 的区别
- mysql 运算符
- Mongo查询语句
- Nginx配置文件详解
- (一) Java多线程详解之多线程概念和创建线程的两种方式