我正在为高容量高速分布式应用编写Kafka Consumer。我只有一个主题,但传入消息的速率非常高。拥有多个服务于更多消费者的分区将适用于此用例。最好的消费方式是拥有多个流阅读器。根据文档或可用样本,ConsumerConnector提供的KafkaStream数量基于主题数量。想知道如何获得多个KafkaStream读者[基于分区],这样我可以跨每个流跨越一个线程或者从多个线程中的同一个KafkaStream读取将从多个分区进行并发读取?
非常感谢任何见解。
答案 0 :(得分:15)
想分享我在邮件列表中找到的内容:
您在主题地图中传递的数字控制主题划分的流数量。在您的情况下,如果传入1,则所有10个分区的数据将被送入1个流。如果传入2,则2个流中的每一个都将从5个分区获取数据。如果你传入11,其中10个将从1个分区中获取数据,1个流将不会获得任何内容。
通常,您需要在自己的线程中迭代每个流。这是因为如果没有新事件,每个流都可以永久阻止。
示例代码段:
topicCount.put(msgTopic, new Integer(partitionCount));
Map<String, List<KafkaStream<byte[], byte[]>>> consumerStreams = connector.createMessageStreams(topicCount);
List<KafkaStream<byte[], byte[]>> streams = consumerStreams.get(msgTopic);
for (final KafkaStream stream : streams) {
ReadTask task = new ReadTask(stream, msgTopic);
task.addObserver(this.msgObserver);
tasks.add(task); executor.submit(task);
}
答案 1 :(得分:4)
建议的方法是拥有一个线程池,以便Java可以为您和每个流处理组织,createMessageStreamsByFilter方法允许您在Runnable中使用它。例如:
int NUMBER_OF_PARTITIONS = 6;
Properties consumerConfig = new Properties();
consumerConfig.put("zk.connect", "zookeeper.mydomain.com:2181" );
consumerConfig.put("backoff.increment.ms", "100");
consumerConfig.put("autooffset.reset", "largest");
consumerConfig.put("groupid", "java-consumer-example");
consumer = Consumer.createJavaConsumerConnector(new ConsumerConfig(consumerConfig));
TopicFilter sourceTopicFilter = new Whitelist("mytopic|myothertopic");
List<KafkaStream<Message>> streams = consumer.createMessageStreamsByFilter(sourceTopicFilter, NUMBER_OF_PARTITIONS);
ExecutorService executor = Executors.newFixedThreadPool(streams.size());
for(final KafkaStream<Message> stream: streams){
executor.submit(new Runnable() {
public void run() {
for (MessageAndMetadata<Message> msgAndMetadata: stream) {
ByteBuffer buffer = msgAndMetadata.message().payload();
byte [] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
//Do something with the bytes you just got off Kafka.
}
}
});
}
在这个例子中,我基本上要求6个线程,因为我知道每个主题有3个分区,我在白名单中列出了两个主题。一旦我们获得了传入流的句柄,我们就可以迭代它们的内容,即MessageAndMetadata对象。元数据实际上只是主题名称和偏移量。正如您发现的那样,如果您要求1个流而不是在我的示例6中,您可以在单个线程中执行此操作,但是如果您需要并行处理,那么很好的方法是为每个返回的流启动一个带有一个线程的执行程序。
答案 2 :(得分:1)
/**
* @param source : source kStream to sink output-topic
*/
private static void pipe(KStream<String, String> source) {
source.to(Serdes.String(), Serdes.String(), new StreamPartitioner<String, String>() {
@Override
public Integer partition(String arg0, String arg1, int arg2) {
return 0;
}
}, "output-topic");
}
上面的代码将在主题名称“output-topic”
的分区1处写入记录