从火花中的kafka消息中获取主题

时间:2016-04-21 13:09:40

标签: apache-spark apache-kafka spark-streaming

在我们的火花流媒体作业中,我们从kafka中读取流媒体中的消息。

为此,我们使用返回KafkaUtils.createDirectStream的{​​{1}} API。

通过以下方式从kafka(来自三个主题 - test1,test2,test3)读取消息:

JavaPairInputDStreamfrom

我们希望以不同的方式处理来自每个主题的消息,为了实现这一点,我们需要知道每条消息的主题名称。

所以我们执行以下操作:

private static final String TOPICS = "test1,test2,test3";
HashSet<String> topicsSet = new HashSet<>(Arrays.asList(TOPICS.split(",")));

HashMap<String, String> kafkaParams = new HashMap<>();
kafkaParams.put("metadata.broker.list", BROKERS);

JavaPairInputDStream<String, String> messages = 
KafkaUtils.createDirectStream(
                streamingContext,
                String.class,
                String.class,
                StringDecoder.class,
                StringDecoder.class,
                kafkaParams,
                topicsSet
                );

这是JavaDStream<String> lines = messages.map(new SplitToLinesFunction());

的实现
SplitToLinesFunction

问题是public class SplitToLinesFunction implements Function<Tuple2<String, String>, String> { @Override public String call(Tuple2<String, String> tuple2) { System.out.println(tuple2._1); return tuple2._2(); } } 为空,我们假设tuple2._1将包含一些元数据,例如消息来自的主题/分区的名称。

但是,当我们打印tuple2._1时,它是空的。

我们的问题 - 有没有办法在kafka中发送主题名称,以便在火花流代码中,tuple2._1将包含它(而不是null)?

请注意,我们还试图从spark-streaming kafka-integration tutorial中提到的DStream中获取主题名称:

但是它返回发送到tuple2._1的所有主题,而不是返回消息(属于当前RDD)的特定主题。

因此,它无法帮助我们确定从RDD中发送消息的主题名称。

修改

回应David的回答 - 我尝试使用这样的KafkaUtils.createDirectStream

MessageAndMetadata

问题是 Map<TopicAndPartition, Long> topicAndPartition = new HashMap(); topicAndPartition.put(new TopicAndPartition("test1", 0), 1L); topicAndPartition.put(new TopicAndPartition("test2", 0), 1L); topicAndPartition.put(new TopicAndPartition("test3", 0), 1L); class MessageAndMetadataFunction implements Function<MessageAndMetadata<String, String>, String> { @Override public String call(MessageAndMetadata<String, String> v1) throws Exception { // nothing is printed here System.out.println("topic = " + v1.topic() + ", partition = " + v1.partition()); return v1.topic(); } } JavaInputDStream<String> messages = KafkaUtils.createDirectStream(streamingContext, String.class, String.class, StringDecoder.class, StringDecoder.class, String.class, kafkaParams, topicAndPartition, new MessageAndMetadataFunction()); messages.foreachRDD(new VoidFunction() { @Override public void call(Object t) throws Exception { JavaRDD<String> rdd = (JavaRDD<String>)t; OffsetRange[] offsets = ((HasOffsetRanges) rdd.rdd()).offsetRanges(); // here all the topics kafka listens to are printed, but that doesn't help for (OffsetRange offset : offsets) { System.out.println(offset.topic() + " " + offset.partition() + " " + offset.fromOffset() + " " + offset.untilOffset()); } } }); 方法中没有打印任何内容。我应该修复什么才能在MessageAndMetadataFunction.call方法中获得该RDD的相关主题?

2 个答案:

答案 0 :(得分:6)

使用以createDirectStream函数作为参数的messageHandler版本之一。这是我的所作所为:

val messages = KafkaUtils.createDirectStream[Array[Byte], Array[Byte], DefaultDecoder, DefaultDecoder, (String, Array[Byte]](
  ssc,
  kafkaParams,
  getPartitionsAndOffsets(topics).map(t => (t._1, t._2._1).toMap,
  (msg: MessageAndMetadata[Array[Byte],Array[Byte]]) => { (msg.topic, msg.message)}
)

那里的东西对你没有任何意义 - 相关部分是

(msg: MessageAndMetadata[Array[Byte],Array[Byte]]) => { (msg.topic, msg.message)}

如果您不熟悉Scala,则所有功能都会返回包含Tuple2msg.topic的{​​{1}}。您的函数需要返回这两个函数才能在下游使用它们。您可以只返回整个msg.message对象,这会为您提供其他一些有趣的字段。但如果您只想要MessageAndMetadatatopic,请使用上述内容。

答案 1 :(得分:1)

Kafka integration guide的底部,有一个从消息中提取主题的示例。

Java中的相关代码:

 // Hold a reference to the current offset ranges, so it can be used downstream
 final AtomicReference<OffsetRange[]> offsetRanges = new AtomicReference<>();

 directKafkaStream.transformToPair(
   new Function<JavaPairRDD<String, String>, JavaPairRDD<String, String>>() {
     @Override
     public JavaPairRDD<String, String> call(JavaPairRDD<String, String> rdd) throws Exception {
       OffsetRange[] offsets = ((HasOffsetRanges) rdd.rdd()).offsetRanges();
       offsetRanges.set(offsets);
       return rdd;
     }
   }
 ).map(
   ...
 ).foreachRDD(
   new Function<JavaPairRDD<String, String>, Void>() {
     @Override
     public Void call(JavaPairRDD<String, String> rdd) throws IOException {
       for (OffsetRange o : offsetRanges.get()) {
         System.out.println(
           o.topic() + " " + o.partition() + " " + o.fromOffset() + " " + o.untilOffset()
         );
       }
       ...
       return null;
     }
   }
 );

这可能会被折叠成更紧凑的东西,只是要求主题而不是别的。