我正在实现一个用于文本分类的流学习器。我的实现中有一些单值参数需要在新流项到达时进行更新。例如,我想在新的预测发生时改变学习率。但是,我怀疑在初始广播之后有一种方法可以广播变量。那么如果我每次更新它时都需要广播变量会发生什么。如果有一种方法可以做到这一点或者解决我想要在Spark Streaming中完成的工作,我会很高兴听到它。
提前致谢。
答案 0 :(得分:2)
我的理解是,一旦广播变量最初被发送出去,它就是“只读”。我相信你可以更新本地节点上的广播变量,但不能更新远程节点上的广播变量。
可能你需要考虑在“Spark之外”这样做。如何使用noSQL商店(Cassandra ..etc)甚至Memcache?然后,您可以从一个任务更新变量,并定期从其他任务中检查此存储?
答案 1 :(得分:2)
我通过在广播变量上创建一个包装类来实现这一点。包装类的updateAndGet方法返回刷新的广播变量。我在dStream.transform中调用了这个函数 - >根据Spark文档
http://spark.apache.org/docs/latest/streaming-programming-guide.html#transform-operation
转换操作状态: "在每个批处理间隔中调用提供的函数。这允许您进行时变RDD操作,即RDD操作,分区数量,广播变量等。可以在批次之间更改。"
BroadcastWrapper类看起来像:
public class BroadcastWrapper {
private Broadcast<ReferenceData> broadcastVar;
private Date lastUpdatedAt = Calendar.getInstance().getTime();
private static BroadcastWrapper obj = new BroadcastWrapper();
private BroadcastWrapper(){}
public static BroadcastWrapper getInstance() {
return obj;
}
public JavaSparkContext getSparkContext(SparkContext sc) {
JavaSparkContext jsc = JavaSparkContext.fromSparkContext(sc);
return jsc;
}
public Broadcast<ReferenceData> updateAndGet(SparkContext sparkContext){
Date currentDate = Calendar.getInstance().getTime();
long diff = currentDate.getTime()-lastUpdatedAt.getTime();
if (var == null || diff > 60000) { //Lets say we want to refresh every 1 min = 60000 ms
if (var != null)
var.unpersist();
lastUpdatedAt = new Date(System.currentTimeMillis());
//Your logic to refresh
ReferenceData data = getRefData();
var = getSparkContext(sparkContext).broadcast(data);
}
return var;
}
}
您可以在允许RDD-RDD转换的stream.transform方法中使用此广播变量updateAndGet函数
objectStream.transform(stream -> {
Broadcast<Object> var = BroadcastWrapper.getInstance().updateAndGet(stream.context());
/**Your code to manipulate stream **/
});
请参阅我的完整答案:https://stackoverflow.com/a/41259333/3166245
希望有所帮助
答案 2 :(得分:1)
我玩得很丑,但是有效! 我们可以找到如何从广播对象获取广播值。 https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/broadcast/TorrentBroadcast.scala#L114 只是通过广播ID。
所以我定期通过相同的广播ID重播。
val broadcastFactory = new TorrentBroadcastFactory()
broadcastFactory.unbroadcast(BroadcastId, true, true)
// append some ids to initIds
val broadcastcontent = broadcastFactory.newBroadcast[.Set[String]](initIds, false, BroadcastId)
我可以从第一个广播值获得BroadcastId。
val ids = ssc.sparkContext.broadcast(initIds)
// broadcast id
val BroadcastId = broadcastIds.id
然后工人正常使用ID作为广播类型。
def func(record: Array[Byte], bc: Broadcast[Set[String]]) = ???
答案 3 :(得分:1)
bkc.unpersist(true)
bkc.destroy()
bkc = sc.broadcast(tableResultMap)
bkv = bkc.value
你可以试试这个,我不保证是否有效
答案 4 :(得分:0)
最好将数据收集到驱动程序,然后将它们广播到所有节点。
使用Dstream # foreachRDD在驱动程序中收集计算出的RDD,一旦知道何时需要更改学习速率,就可以使用SparkContext#broadcast(value)将新值发送到所有节点。
我希望代码看起来像下面这样:
dStreamContainingBroadcastValue.foreachRDD{ rdd =>
val valueToBroadcast = rdd.collect()
sc.broadcast(valueToBroadcast)
}
您还可以从spark用户邮件列表中找到有用的this thread。如果有效,请告诉我。