我正试图掌握Spark Streaming,但我遇到了困难。尽管阅读了文档并分析了示例,但我希望在文本文件/流/ Kafka队列中执行更多的操作,这是我们唯一可以从文档中理解的内容。
我希望收听传入的Kafka消息流,按键分组消息然后处理它们。下面的代码是该过程的简化版本;从Kafka获取消息流,按键减少消息密钥对消息进行分组,然后处理它们。
JavaPairDStream<String, byte[]> groupByKeyList = kafkaStream.reduceByKey((bytes, bytes2) -> bytes);
groupByKeyList.foreachRDD(rdd -> {
List<MyThing> myThingsList = new ArrayList<>();
MyCalculationCode myCalc = new MyCalculationCode();
rdd.foreachPartition(partition -> {
while (partition.hasNext()) {
Tuple2<String, byte[]> keyAndMessage = partition.next();
MyThing aSingleMyThing = MyThing.parseFrom(keyAndMessage._2); //parse from protobuffer format
myThingsList.add(aSingleMyThing);
}
});
List<MyResult> results = myCalc.doTheStuff(myThingsList);
//other code here to write results to file
});
调试时我发现在(partition.hasNext())
myThingsList
内,List<MyThing> myThingsList
的内存地址与外部forEachRDD
中声明的List<MyResult> results = myCalc.doTheStuff(myThingsList);
的内存地址不同。
调用myThingsList
时没有结果,因为#include <iostream>
#include <iomanip>
bool g( const int a[], int n )
{
int long long sum = 0;
int i = 0;
for ( ; i < n && a[i] > 0 ; i++ ) sum += a[i];
return i == n && sum == ( long long int )n * ( n + 1 ) / 2;
}
int main()
{
const int N = 6;
int t[N] = { 6, 2, 3, 4, 5, 1 };
std::cout << std::boolalpha << g( t, N ) << std::endl;
}
是List的不同实例。
我想要一个解决这个问题的方法但是更喜欢参考文档来帮助我理解为什么这不起作用(如预期的那样)以及我如何为自己解决它(我不是指链接到Spark文档的单页,还有section / paragraph,或者最好仍然是指向'JavaDoc'的链接,它不提供带有非功能注释代码的Scala示例。)
答案 0 :(得分:1)
您看到不同列表地址的原因是因为Spark不在驱动程序上本地执行foreachPartition
,它必须序列化该函数并通过处理分区处理的Executor发送它。您必须记住,尽管使用代码感觉就像所有内容都在一个位置运行一样,但计算实际上是分布式的。
我看到代码中的第一个问题与你的reduceByKey
有关,它需要两个字节数组并返回第一个,这真的是你想要做的吗?这意味着您正在有效地删除部分数据,也许您正在寻找combineByKey
,这将允许您返回JavaPairDStream<String, List<byte[]>
。
关于你的protobuf的解析,看起来就像你不想要foreachRDD
,你需要额外的map
来解析数据:
kafkaStream
.combineByKey(/* implement logic */)
.flatMap(x -> x._2)
.map(proto -> MyThing.parseFrom(proto))
.map(myThing -> myCalc.doStuff(myThing))
.foreachRDD(/* After all the processing, do stuff with result */)