我正在做一个Flink项目,遇到了一个问题,我设法通过Stackoverflow的答案解决了这个问题。但是,对我来说,为什么尚不清楚提议的解决方案是否真正起作用,并且我发现有关该主题的信息很少。考虑以下代码:
object DeCP {
def main(args: Array[String]): Unit = {
val params: ParameterTool = ParameterTool.fromArgs(args)
// Get the execution environment and read the data
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
val queryPoints: DataSet[Point] = readQueryPoints(env, params)
val points: DataSet[Point] = readFeatureVector(env, params)
// Process the query points
queryPoints
.map(new KNNRich)
.withBroadcastSet(points, "pointsIn")
.print
}
final class KNNRich extends RichMapFunction[Point, (Point, Vector[Point])]{
private var pointsIn: Traversable[Point] = _
override def open(parameters: Configuration): Unit =
pointsIn = getRuntimeContext.getBroadcastVariable[Point]("pointsIn").asScala
def map(queryPoint: Point): (Point, Vector[Point]) = {
val dataSetIn = ExecutionEnvironment.getExecutionEnvironment
.fromCollection(pointsIn.toVector)
val cluster = new Cluster(dataSetIn, queryPoint)
val knn = cluster.kNearestNeighbor(queryPoint, 3) // This call causes problems
(queryPoint, knn.collect.toVector)
}
}
}
Cluster类和伴随对象的定义为:
class Cluster(var points: DataSet[Point],
var clusterLeader: Point) extends Serializable {
private var queryPoint: Point = _
def distance(p: Point): Point = {
p.eucDist(queryPoint)
}
def kNearestNeighbor(queryPoint: Point, k: Int): DataSet[Point] = {
this.queryPoint = queryPoint
this.points.map{p => distance(p)} // Task not serializable
this.points.map{p => p.eucDist(queryPoint)} // Works
this.points.map{p => Cluster.staticDistance(queryPoint, p)} // Works
}
}
object Cluster {
def staticDistance(queryPoint: Point, p: Point): Point = {
p.eucDist(queryPoint)
}
}
对distance
方法的调用会导致任务无法序列化的异常,但是用定义替换方法调用可以解决此问题。类似地,将完全相同的方法定义为伴随对象的成员可以使代码正常运行。
为什么第一个电话不起作用,而另两个电话却起作用?如果您在类上执行更复杂的流程,而又不容易将其替换为伴随对象上的方法,会发生什么?
答案 0 :(得分:1)
通过执行DataSet转换,您只是在创建管道的逻辑计划。通过调用execute/print/collect
将管道提交给集群。
将管道提交给集群时,每个函数(如RichMapFunction
)都会被序列化,发送到集群,为每个并行实例复制并独立执行。当您收到“无法序列化的任务”异常时,这意味着您的RichMapFunction
正在传递引用该类之外的变量/对象。您应该确保一个函数是一个独立的块。
通过调用points.map{}
,您将隐式创建一个MapFunction
。但是此MapFunction
引用了Cluster
的实例,因此不是独立的。 Flink也尝试序列化Cluster
,但失败。如果distance
是静态的(在伴随对象中定义),则Cluster
也不需要序列化。
顺便说一句,您示例的另一个问题是您没有按预期使用DataSet API。通常,不应在正在运行的管道中创建管道。这也可能导致意外的副作用。