使用java实现Kafka的消费者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package com.lisg.kafkatest;
 
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
 
import kafka.consumer.Consumer;
import kafka.consumer.ConsumerConfig;
import kafka.consumer.ConsumerIterator;
import kafka.consumer.KafkaStream;
import kafka.javaapi.consumer.ConsumerConnector;
 
/**
 * java实现Kafka消费者的示例
 * @author lisg
 *
 */
public class KafkaConsumer {
    private static final String TOPIC = "test";
    private static final int THREAD_AMOUNT = 1;
 
    public static void main(String[] args) {
         
        Properties props = new Properties();
        props.put("zookeeper.connect", "vm1:2181");
        props.put("group.id", "group1");
        props.put("zookeeper.session.timeout.ms", "400");
        props.put("zookeeper.sync.time.ms", "200");
        props.put("auto.commit.interval.ms", "1000");;
         
        Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
        //每个topic使用多少个kafkastream读取, 多个consumer
        topicCountMap.put(TOPIC, THREAD_AMOUNT);
        //可以读取多个topic
//      topicCountMap.put(TOPIC2, 1);
        ConsumerConnector consumer = Consumer.createJavaConsumerConnector(new ConsumerConfig(props));
        Map<String, List<KafkaStream<byte[], byte[]>>> msgStreams = consumer.createMessageStreams(topicCountMap );
        List<KafkaStream<byte[], byte[]>> msgStreamList = msgStreams.get(TOPIC);
         
        //使用ExecutorService来调度线程
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_AMOUNT);
        for (int i = 0; i < msgStreamList.size(); i++) {
            KafkaStream<byte[], byte[]> kafkaStream = msgStreamList.get(i);
            executor.submit(new HanldMessageThread(kafkaStream, i));
        }
         
         
        //关闭consumer
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (consumer != null) {
            consumer.shutdown();
        }
        if (executor != null) {
            executor.shutdown();
        }
        try {
            if (!executor.awaitTermination(5000, TimeUnit.MILLISECONDS)) {
                System.out.println("Timed out waiting for consumer threads to shut down, exiting uncleanly");
            }
        } catch (InterruptedException e) {
            System.out.println("Interrupted during shutdown, exiting uncleanly");
        }
    }
 
}
 
/**
 * 具体处理message的线程
 * @author Administrator
 *
 */
class HanldMessageThread implements Runnable {
 
    private KafkaStream<byte[], byte[]> kafkaStream = null;
    private int num = 0;
     
    public HanldMessageThread(KafkaStream<byte[], byte[]> kafkaStream, int num) {
        super();
        this.kafkaStream = kafkaStream;
        this.num = num;
    }
 
    public void run() {
        ConsumerIterator<byte[], byte[]> iterator = kafkaStream.iterator();
        while(iterator.hasNext()) {
            String message = new String(iterator.next().message());
            System.out.println("Thread no: " + num + ", message: " + message);
        }
    }
     
}
1
props.put("auto.commit.interval.ms", "1000");

表示的是:consumer间隔多长时间在zookeeper上更新一次offset

说明:

为什么使用High Level Consumer?

有些场景下,从Kafka中读取消息的逻辑不处理消息的offset,仅仅是获取消息数据。High Level Consumer就提供了这种功能。

首先要知道的是,High Level Consumer在ZooKeeper上保存最新的offset(从指定的分区中读取)。这个offset基于consumer group名存储。

Consumer group名在Kafka集群上是全局性的,在启动新的consumer group的时候要小心集群上没有关闭的consumer。当一个consumer线程启动了,Kafka会将它加入到相同的topic下的相同consumer group里,并且触发重新分配。在重新分配时,Kafka将partition分配给consumer,有可能会移动一个partition给另一个consumer。如果老的、新的处理逻辑同时存在,有可能一些消息传递到了老的consumer上。

设计High Level Consumer

使用High LevelConsumer首先要知道的是,它应该是多线程的。消费者线程的数量跟tipic的partition数量有关,它们之间有一些特定的规则:

  • 如果线程数量大于主题的分区数量,一些线程将得不到任何消息

  • 如果分区数大于线程数,一些线程将得到多个分区的消息

  • 如果一个线程处理多个分区的消息,它接收到消息的顺序是不能保证的。比如,先从分区10获取了5条消息,从分区11获取了6条消息,然后从分区10获取了5条,紧接着又从分区10获取了5条,虽然分区11还有消息。

  • 添加更多了同consumer group的consumer将触发Kafka重新分配,某个分区本来分配给a线程的,从新分配后,有可能分配给了b线程。

关闭消费组和错误处理

Kafka不会再每次读取消息后马上更新zookeeper上的offset,而是等待一段时间。由于这种延迟,有可能消费者读取了一条消息,但没有更新offset。所以,当客户端关闭或崩溃后,从新启动时有些消息重复读取了。另外,broker宕机或其他原因导致更换了partition的leader,也会导致消息重复读取。

为了避免这种问题,你应该提供一个平滑的关闭方式,而不是使用kill -9

上面的java代码中提供一种关闭的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (consumer != null) {
    consumer.shutdown();
}
if (executor != null) {
    executor.shutdown();
}
try {
    if (!executor.awaitTermination(5000, TimeUnit.MILLISECONDS)) {
        System.out.println("Timed out waiting for consumer threads to shut down, exiting uncleanly");
    }
} catch (InterruptedException e) {
    System.out.println("Interrupted during shutdown, exiting uncleanly");
}

在shutdown之后,等待了5秒钟,给consumer线程时间来处理完kafka stream里保留的消息。

参考资料:https://cwiki.apache.org/confluence/display/KAFKA/Consumer+Group+Example

附件列表

java实现Kafka的消费者示例的更多相关文章

  1. kafka集群搭建和使用Java写kafka生产者消费者

    1 kafka集群搭建 1.zookeeper集群  搭建在110, 111,112 2.kafka使用3个节点110, 111,112 修改配置文件config/server.properties ...

  2. Java阻塞队列(BlockingQueue)实现 生产者/消费者 示例

    Java阻塞队列(BlockingQueue)实现 生产者/消费者 示例 本文由 TonySpark 翻译自 Javarevisited.转载请参见文章末尾的要求. Java.util.concurr ...

  3. java实现Kafka生产者示例

    使用java实现Kafka的生产者 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 3 ...

  4. Java版Kafka使用及配置解释

    Java版Kafka使用及配置解释 一.Java示例 kafka是吞吐量巨大的一个消息系统,它是用scala写的,和普通的消息的生产消费还有所不同,写了个demo程序供大家参考.kafka的安装请参考 ...

  5. 初始 Kafka Consumer 消费者

    温馨提示:整个 Kafka 专栏基于 kafka-2.2.1 版本. 1.KafkaConsumer 概述 根据 KafkaConsumer 类上的注释上来看 KafkaConsumer 具有如下特征 ...

  6. 第23章 java线程通信——生产者/消费者模型案例

    第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...

  7. RocketMQ消费者示例程序

    转载请注明出处:http://www.cnblogs.com/xiaodf/ 本博客实现了一个简单的RocketMQ消费者的示例,MQ里存储的是经过Avro序列化的消息数据,程序读取数据并反序列化后, ...

  8. JAVA版Kafka代码及配置解释

    伟大的程序员版权所有,转载请注明:http://www.lenggirl.com/bigdata/java-kafka.html.html 一.JAVA代码 kafka是吞吐量巨大的一个消息系统,它是 ...

  9. Java操作Kafka

    java操作kafka非常的简单,然后kafka也提供了很多缺省值,一般情况下我们不需要修改太多的参数就能使用.下面我贴出代码. pom.xml <dependency> <grou ...

随机推荐

  1. sql server 只读帐号设置能读取存储过程,view等内容。

    如果设定了某个帐号是只读权限,是无法查看存储过程的,比如你用 sp_helptext '存储过程名' ,这样子会提示权限不足或者存储过程不存在,其实就是没权限, 要再给这个只读帐号用 sa 赋予它查看 ...

  2. Redis查询&JDBC查询&Hibernate查询方式的效率比较...

    比较三种查询方式查询效率对比...我是用的JavaWeb的方式通过通过JSP页面查询的填写查询的参数...给予反馈.... 整个demo的下载地址:http://files.cnblogs.com/f ...

  3. Observer观察者设计模式

    Observer设计模式主要包括以下两种对象: (1)被观察对象:Subject,它往往包含其他对象感兴趣的东西,上面例子中热水器中就是Subject(被监视对象); (2)观察对象:Observer ...

  4. java对象流与序列化

    Object流,直接把obj写入或读出. 前言: 比如 画图的程序,咣当画一个三角形出来,咣当画一正方形出来.然后存盘,当你下次再打开软件的时候三角形.方块还在原来的位置上.如果用面向对象的思维,三角 ...

  5. Exception message: /bin/bash: line 0: fg: no job control

    这个错误是 我本地idea 远程调试hadoop集群出现的 Diagnostics: Exception from container-launch. Container id: container_ ...

  6. Windows Server2016服务器系统创建域服务器

    原先是有图片,因为图片是直接粘贴上来的,发布之后图片都看见了,然后都使用上传图片的方式才搞定,这也是博客园比较坑的地方: 也可以查看该链接含图片: http://www.cnblogs.com/all ...

  7. C/C++ -- Gui编程 -- Qt库的使用 -- 标准对话框

    -----mywidget.cpp----- #include "mywidget.h" #include "ui_mywidget.h" #include & ...

  8. 开发工具 -- PyDev 在 Eclipse中的安装

    1. 将从sorceforge下载到的PyDev3.4.1解压后放到eclipse的插件目录下F:\APP\IDE\Java\Eclipse\eclipse-java-kepler-SR2-win32 ...

  9. springcloud-06-feign的使用

    在spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端.我们可以使用JDK原生的URLConnection.Ap ...

  10. AE开发流程

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...