1. Comprensión de los cierres de Scala
Un cierre es una función cuyo valor de retorno depende de una o más variables declaradas fuera de la función. Los cierres generalmente se pueden considerar simplemente como otra función que puede acceder a variables locales en una función.
Como la siguiente función anónima:
val multiplier = (i:Int) => i * 10
Hay una variable i en el cuerpo de la función, que sirve como parámetro de la función. Como otro fragmento de código a continuación:
val multiplier = (i:Int) => i * factor
En multiplier
Hay dos variables: i y factor. Donde i es una forma de una función de los parámetros, la multiplier
función se llama, se me ha dado un nuevo valor. Sin embargo, el factor no es un parámetro formal, sino una variable libre. Considere el siguiente código:
var factor = 3 val multiplier = (i:Int) => i * factor
Aquí introducimos una variable libre factor
, que se define fuera de la función.
Variables de función así definidas multiplier
como un "cierre", ya que se refiere a la variable fuera de la definición de función, la definición de esta función de proceso es capturar la variable libre para formar una función cerrada
Ejemplo completo:
object Test {
def main(args: Array[String]) {
println( "muliplier(1) value = " + multiplier(1) )
println( "muliplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
}
2. Comprensión de los cierres en Spark
Primero mire el siguiente fragmento de código:
val data=Array(1, 2, 3, 4, 5)
var counter = 0
var rdd = sc.parallelize(data)
// ???? 这样做会怎么样
rdd.foreach(x => counter += x)
println("Counter value: " + counter)
Lo primero que hay que asegurarse es que el resultado de la salida anterior es 0, y park descompone el procesamiento de las operaciones RDD en tareas, y cada tarea se Executor
ejecuta. Antes de la ejecución, Spark calcula el cierre de la tarea. Los cierres son Executor
aquellas variables y métodos que deben estar visibles al realizar cálculos en el RDD (foreach () en este caso). El cierre se serializará y se enviará a cada Ejecutor, pero la copia se enviará al Ejecutor, por lo que la salida en el Controlador sigue siendo counter
ella misma, si desea actualizar el global, use el acumulador y spark-streaming
utilícelo updateStateByKey
para actualizar el estado público.
Además, los cierres en Spark tienen otras funciones.
1. Borre las variables globales inútiles enviadas por el controlador al ejecutor y solo copie la información de la variable útil al ejecutor
2. Asegúrese de que los datos enviados al Ejecutor sean datos serializados
Por ejemplo, al usar DataSet, la definición de la clase de caso debe estar debajo de la clase, no dentro del método. Incluso si no hay ningún problema con la sintaxis, si ha usado json4s para serializar, implicit val formats = DefaultFormats
la importación debe colocarse debajo de la class, de lo contrario, la secuencia de formato debe ser separada incluso si no la usa para nada más.
3. Resumen
Los cierres son visibles en todas partes en todo el ciclo de vida de Spark, por ejemplo Driver
, todos los datos copiados desde arriba deben ser serializados + cierres Executor
.
Wu Xie, Xiao San Ye, un pequeño novato en segundo plano, big data e inteligencia artificial. Presta atención a más