获取队列中的所有kafka消息并在java中停止流式传输

时间:2016-07-29 15:21:08

标签: java apache-kafka kafka-consumer-api

我需要在晚上执行一个Job,它将获取kafka队列中的所有消息并使用它们执行一个进程。我能够收到消息,但是kafka流正在等待更多消息,我无法继续我的进程。我有以下代码:

...
private ConsumerConnector consumerConnector;
private final static String TOPIC = "test";

public MessageStreamConsumer() {
        Properties properties = new Properties();
        properties.put("zookeeper.connect", "localhost:2181");
        properties.put("group.id", "test-group");
        ConsumerConfig consumerConfig = new ConsumerConfig(properties);
        consumerConnector = Consumer.createJavaConsumerConnector(consumerConfig);
    }
public List<String> getMessages() {
                Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
                topicCountMap.put(TOPIC, new Integer(1));
                Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = consumerConnector
                        .createMessageStreams(topicCountMap);
                KafkaStream<byte[], byte[]> stream = consumerMap.get(TOPIC).get(0);
                ConsumerIterator<byte[], byte[]> it = stream.iterator();
                List<String> messages = new ArrayList<>();
                while (it.hasNext())
                    messages.add(new String(it.next().message()));
                return messages;
            }

代码能够获取消息,但是当它处理最后一条消息时,它仍然保留在行中:

 while (it.hasNext())

问题是,如何从kafka获取所有消息,停止流并继续执行其他任务。

我希望你能帮助我

由于

3 个答案:

答案 0 :(得分:0)

似乎kafka流不支持从头开始消费 你可以创建一个本地kafka消费者并将auto.offset.reset设置为最早,然后它将从开始消费消息。

答案 1 :(得分:0)

这样的事可能有用。基本上我的想法是使用Kafka消费者和民意调查,直到你得到一些记录,然后当你得到一个空的批次时停止。

package kafka.examples;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;


public class Consumer1 extends Thread
{
    private final KafkaConsumer<Integer, String> consumer;
    private final String topic;
    private final DateFormat df;
    private final String logTag;
    private boolean noMoreData = false;
    private boolean gotData = false;
    private int messagesReceived = 0;
    AtomicBoolean isRunning = new AtomicBoolean(true);
    CountDownLatch shutdownLatch = new CountDownLatch(1);

    public Consumer1(Properties props)
    {
        logTag = "Consumer1";

        consumer = new KafkaConsumer<>(props);
        this.topic = props.getProperty("topic");
        this.df = new SimpleDateFormat("HH:mm:ss");

        consumer.subscribe(Collections.singletonList(this.topic));
    }

    public void getMessages() {
        System.out.println("Getting messages...");
        while (noMoreData == false) {
            //System.out.println(logTag + ": Doing work...");

            ConsumerRecords<Integer, String> records = consumer.poll(1000);
            Date now = Calendar.getInstance().getTime();
            int recordsCount = records.count();
            messagesReceived += recordsCount;
            System.out.println("recordsCount: " + recordsCount);
            if (recordsCount > 0) {
               gotData = true;
            }

            if (gotData && recordsCount == 0) {
                noMoreData = true;
            }

            for (ConsumerRecord<Integer, String> record : records) {
                int kafkaKey = record.key();
                String kafkaValue = record.value();
                System.out.println(this.df.format(now) + " " + logTag + ":" +
                        " Received: {" + kafkaKey + ":" + kafkaValue + "}" +
                        ", partition(" + record.partition() + ")" +
                        ", offset(" + record.offset() + ")");
            }
        }
        System.out.println("Received " + messagesReceived + " messages");
    }

    public void processMessages() {
        System.out.println("Processing messages...");
    }

    public void run() {
        getMessages();
        processMessages();
    }
}

答案 2 :(得分:0)

我目前正在使用Kafka 0.10.0.1进行开发,并发现有关使用消费者属性 auto.offset.reset 的混合信息,所以我做了一些实验来弄清楚究竟发生了什么。< / p>

基于这些,我现在就这样理解了:当你设置属性时:

auto.offset.reset=earliest

这将消费者定位为分配的分区中的第一个可用消息(当没有对分区进行提交时)或者将消费者定位在最后提交的分区偏移量(请注意,您始终提交最后一个读取偏移量+ 1否则你将在每次重启消费者时重新阅读最后提交的消息)

或者,您不要设置 auto.offset.reset ,这意味着将使用默认值'latest'。

在这种情况下,您不会收到任何关于连接消费者的旧消息 - 只有在连接消费者后才会发布到该主题的消息。

作为结论 - 如果您想确保接收特定主题和分配的分区的所有可用消息,则必须调用seekToBeginning()。

似乎建议首先调用poll(0L)以确保您的使用者获得分配的分区(或在ConsumerRebalanceListener中实现您的代码!),然后将每个分配的分区寻找为“开始”:

kafkaConsumer.poll(0L);
kafkaConsumer.seekToBeginning(kafkaConsumer.assignment());