在我们的火花流媒体作业中,我们从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的相关主题?
答案 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
,则所有功能都会返回包含Tuple2
和msg.topic
的{{1}}。您的函数需要返回这两个函数才能在下游使用它们。您可以只返回整个msg.message
对象,这会为您提供其他一些有趣的字段。但如果您只想要MessageAndMetadata
和topic
,请使用上述内容。
答案 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;
}
}
);
这可能会被折叠成更紧凑的东西,只是要求主题而不是别的。