Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Workshop Apache Flink Madrid

1,572 views

Published on

Material utilizado en la segunda reunión del Meetup Apache Flink Madrid. Taller de iniciación a la programación Big Data con Apache Flink.

Published in: Technology

Workshop Apache Flink Madrid

  1. 1. Workshop Apache Flink Dr. Rubén Casado @ruben_casado #2. 20 de Mayo de 2016 ruben.casado@outlook.com
  2. 2. Apache Flink • Stack tecnológico desarrollado inicialmente como proyecto de I+D Stratosphere por grupos de investigación de Berlín. Apache Incubator en Abril 2014 y Apache Top Level en Diciembre 2014. • Motor de procesamiento en memoria • Procesamiento de streaming puro. Batch es un tipo concreto de Streaming. • API similar a Spark. • Soporte nativo de iteraciones. • Híbrido mediante Arquitectura Kappa. • Ecosistema creciendo.
  3. 3. Ejemplos • Batch • Stream
  4. 4. Librerías Ingesta y almacenamiento de datos Motores de procesamiento Gestores de aplicaciones y recursos Clasificación YARN
  5. 5. Comparación con otras tecnologías API low-level high-level high-level Data Transfer batch batch pipelined & batch Memory Management disk-based JVM-managed Active managed Iterations file system cached in-memory cached streamed Fault tolerance task level task level job level Good at massive scale out data exploration heavy backend & iterative jobs Libraries many external built-in & external evolving built-in & external Batch processing Streaming “true” mini batches “true” API low-level high-level high-level Fault tolerance tuple-level ACKs RDD-based (lineage) coarse checkpointing State not built-in external internal Exactly once at least once exactly once exactly once Windowing not built-in restricted flexible Latency low medium low Throughput medium high high Streaming processing
  6. 6. Tecnologías Streaming Si Micro-batches Si Si Gestión de estado No Si. Escribe a disco. Si. Escribe a disco. Si. BD embebida. Semántica at least once exactly-once con Trident exactly once exactly once at least once Ventana deslizante Sí en 1.0 Si. Por tiempo Si. Por tiempo y nº eltos No Ventana no- deslizante Sí en 1.0 Si. Por tiempo (micro-batch) Si. Por tiempo y nº eltos. Si. Por tiempo Latencia < segundos segundos < segundos < segundos Rendimiento ¿Medio? Alto Alto Alto Lenguajes Java, Scala, Ruby, Python, Javascript, Perl Scala, Java, Python, R Java, Scala, Python Java, Scala Madurez ¿Alta? Media Baja Baja
  7. 7. 1. Instalar Java 1.8 sudo add-apt-repository ppa:openjdk-r/ppa sudo apt-get update sudo apt-get install openjdk-8-jdk Editar etc/environment para añadir la variable JAVA_HOME JAVA_HOME=“/usr/lib/jvm/java-8-openjdk-XXX” 2. Instalar Scala 2.11 sudo apt-get remove scala-library scala sudo wget www.scala-lang.org/files/archive/scala-2.11.8.deb sudo dpkg -i scala-2.11.8.deb [Si da errores] sudo apt-get install libjansi-java [Si da errores] sudo apt-get -f install sudo apt-get update sudo apt-get install scala 3. Instalar maven sudo apt-get install maven 4. Descargar y descomprimir Flink 1.0.2 wget http://apache.rediris.es/flink/flink-1.0.2/flink-1.0.2-bin-hadoop27-scala_2.11.tgz tar -zxvf flink-1.0.2-bin-hadoop27-scala_2.11.tgz 5. Probar Scala-Flink interactivo bin/start-scala-shell.sh local Preparación del entorno (1/2)
  8. 8. 6. Crear un esqueleto maven mvn archetype:generate -DarchetypeGroupId=org.apache.flink -DarchetypeArtifactId=flink-quickstart-java -DarchetypeVersion=1.0.0 -DgroupId=org.apache.flink.quickstart -DartifactId=flink-java-project -Dversion=0.1 -Dpackage=org.apache.flink.quickstart -DinteractiveMode=false cd flink-java-project mvn clean install mvn clean package 7. Instalar IDE. Importar proyecto en el IDE [Intelli J] Import project → Import project from external model [existing sources]→ Maven 8. Ejecutar WordCount para validar instalación y abrir localhost:8081 para ver la app de admin 9. Desplegar en cluster mvn clean package → en /target obtenemos el .jar flink-1.0.1/bin/start-local.sh → arrancar cluster local, existen otras opciones [opcional] flink-1.0.1/ tail log flink-*-jobmanager* → ver si todo está OK flink-1.0.1/bin/flink run –c MainClassName ../target/flink-java-project-0.1.jar → lanzar job Preparación del entorno (2/2) mvn archetype:generate -DarchetypeGroupId=org.apache.flink -DarchetypeArtifactId=flink-quickstart-java -DarchetypeVersion=1.0.0 -DgroupId=org.apache.flink.quickstart -DartifactId=flink-java-project -Dversion=0.1 -Dpackage=org.apache.flink.quickstart -DinteractiveMode=false http://dataartisans.github.io/flink-training/devSetup/handsOn.html
  9. 9. • Client • Master (Job Manager) • Worker (Task Manager) Arquitectura Client Job Manager Task Manager Task Manager Task Manager
  10. 10. • Local. Similar al Driver de Spark • Construye el grafo de transformaciones (DAF) y lo optimiza • Pasa el grafo al Job Manager • Recoge los resultados Job Manager Client case class Path (from: Long, to: Long) val tc = edges.iterate(10) { paths: DataSet[Path] => val next = paths .join(edges) .where("to") .equalTo("from") { (path, edge) => Path(path.from, edge.to) } .union(paths) .distinct() next } Optimizer Type extraction Data Source orders.tbl Filter Map DataSource lineitem.tbl Join Hybrid Hash buildHT probe hash-part [0] hash-part [0] GroupRed sort forward Client
  11. 11. • Paralelizacion: Crea el grafo de ejecución • Scheduling: Asigna las tasks a los task managers • State: Supervisa la ejecución Job Manager Job Manager Data Source orders.tbl Filter Map DataSour ce lineitem.tbl Join Hybrid Hash build HT prob e hash-part [0] hash-part [0] GroupRed sort forwar d Task Manager Task Manager Task Manager Task Manager Data Source orders.tbl Filter Map DataSour ce lineitem.tbl Join Hybrid Hash build HT prob e hash-part [0] hash-part [0] GroupRed sort forwar d Data Source orders.tbl Filter Map DataSour ce lineitem.tbl Join Hybrid Hash build HT prob e hash-part [0] hash-part [0] GroupRed sort forwar d Data Source orders.tbl Filter Map DataSour ce lineitem.tbl Join Hybrid Hash build HT prob e hash-part [0] hash-part [0] GroupRed sort forwar d Data Source orders.tbl Filter Map DataSour ce lineitem.tbl Join Hybrid Hash build HT prob e hash-part [0] hash-part [0] GroupRed sort forwar d
  12. 12. • Las operaciones se dividen en tasks según el grado de paralelismo definido • Cada instancia paralela de una operación ejecuta la operación en un task slot separado • El scheduler puede ejecutar diversas tasks de diferentes operaciones en el mismo task slot Task Manager S l o t Task ManagerTask Manager S l o t S l o t Task Manager
  13. 13. 13 Gelly Table ML SAMOA DataSet (Java/Scala/Python) DataStream (Java/Scala) HadoopM/R Local Remote Yarn Tez Embedded Dataflow Dataflow(WiP) MRQL Table Cascading(WiP) Streaming dataflow runtime DataSet
  14. 14. • Lista distribuida de elementos en memoria  similar a RDD de Spark • Los elementos pueden ser simples (String, Long, Integer, Boolean) o compuestos (Array, Tuple, Pojo) • Se crea a partir de un ExecutionEnviorment o tras aplicar transformaciones a otro DataSet • Existen diferentes subclases de DataSet con sus peculiaridades • Data Source → Orígenes de datos desde lo que un ExecutionEnviorment puede generar un Dataset (p.e. fichero) • Data Sink → Destino de datos donde se puede volcar un DataSet (p.e. fichero) DataSet
  15. 15. Data Source DataSet Data Sink DataSet • API proporcionada por el ExecutionEnviorment • Genera objetos DataSet a partir de una fuente de datos (fichero, base de datos, etc.) • Se puede añadir Data Source nuevos • Estructura de datos básica en Flink para batch processing • Proporciona el API para aplicar transformaciones sobre un DataSet y generar nuevos DataSets (o clases derivadas) • API proporcionada por la clase DataSet • Permite exportar el contenido de un DataSet hacia otro sistema (fichero, base de datos, etc.) • Se puede añadir Data Sinks nuevos
  16. 16. object WordCount { def main(args: Array[String]) { // Crear el ExecutionEnvironment val env = ExecutionEnvironment.getExecutionEnvironment // Crear un Dataset con un DataSource val text = env.fromElements("hola", "qué tal", "Apache Flink") // Transformaciones sobre un DataSet val counts = text.flatMap { _.toLowerCase.split(“ ") } .map { (_, 1) } .groupBy(0) .sum(1) // Usar un DataSink para conseguir resultados counts.print() } } DataSet
  17. 17. DataSet API DataSet: yDataSet: x val x = env.fromElements(“b”, “a”, “c”) val y = x.map( e =>(e,1)) x.print() y.print() Devuelve un nuevo DataSet aplicando una función a cada uno de los elementos del DataSet original
  18. 18. DataSet API DataSet: yDataSet: x val x = env.fromElements(1, 2, 3) val y = x.filter( e => e%2==1) x.print() y.print() Devuelve un nuevo DataSet que solo incluye los elementos que cumplen la condición
  19. 19. DataSet API DataSet: yDataSet: x Devuelve un nuevo DataSet aplicando una función a cada uno de los elementos del DataSet original y después apilando los resultados. val x = env.fromElements(1, 2, 3) val y = x.flatMap( e => Array (e, e*100, 42)) x.print() y.print()
  20. 20. x = sc.parallelize([1,2,3,4]) y = x.collect() DataSet API DataSet: x val x = env.fromElements(1, 2, 0) x.count() Devuelve un Long con el nº de elementos del DataSet 3 COUNT
  21. 21. x = sc.parallelize([1,2,3,4]) y = x.collect() DataSet API DataSet: x val x = env.fromElements(1, 2, 0) x.print() Imprime por la salida estándar (System.out) los elementos del DataSet 1 2 0 PRINT
  22. 22. • ¿En cuántas líneas del fichero LICENSE aparece la palabra “contributor”? • Tips  • val mydataset = env.readTextFile(“LICENSE”) • Función string.contains(string2)de Scala • Si se quiere tener en cuenta apariciones tanto en minúscula como en mayúscula: string.toLowerCase() Ejercicio 1 DataSet API
  23. 23. Tipos de Datos para DataSet • Tipos básicos de Java – String, Long, Integer, Boolean,… – Arrays • Tipos compuestos – Tuples – Pojo – Scala Case class • No todos los tipos pueden ser usados como claves – Tienen que ser comparables 23
  24. 24. Tuples • La manera más fácil y eficiente de encapsular datos en Flink • Tuple1 hasta Tuple25 Tuple2<String, String> person = new Tuple2<String, String>("Max", "Mustermann”); Tuple3<String, String, Integer> person = new Tuple3<String, String, Integer>("Max", "Mustermann", 42); Tuple4<String, String, Integer, Boolean> person = new Tuple4<String, String, Integer, Boolean>("Max", "Mustermann", 42, true); // el primer campo es el 0 String firstName = person.f0; String secondName = person.f1; Integer age = person.f2; Boolean fired = person.f3; 24
  25. 25. Pojo • Cualquier clase Java que – Tenga un constructor vacío por defecto – Todos sus campos son accesibles (public o getter/setter) public class Person { public int id; public String name; public Person() {}; public Person(int id, String name) {…}; } DataSet<Person> d = env.fromElements(new Person(1, ”Ruben”)); • Permite definir claves por nombre DataSet<Person> p = … // agrupar por el campo “name” d.groupBy(“name”).groupReduce(…); 25
  26. 26. Scala Case Classes • Soporte nativo para case classes de Scala case class Person(id: Int, name: String) d: DataSet[Person] = env.fromElements(new Person(1, “Ruben”) • Permite definer claves por nombre // agrupar por el campo “name” d.groupBy(“name”).groupReduce(…) 26
  27. 27. DataSet: x val x = env.fromElements((1,5), (2,5), (1, 4)) val y =x.groupBy(0) x.print() y.sum(1).print() GroupedDataSet: y (1,5), (2,5), (1, 4) (1,9), (2,5) Devuelve un nuevo GroupedDataSet agrupando los elementos por la clave especificada. La clave puede ser un campo de un Tuple/Pojo/Case o una función que genere la clave. DataSet API
  28. 28. DataSet API GroupedDataset / DataSet: x DataSet: y Devuelve un DataSet de un único elemento combinando todos los elementos del DataSet original mediante una función asociativa. Se puede aplicar en GroupedDataset, generando en este caso un DataSet con un elemento por cada clave. val x = env.fromElements(1,2,3,4) val y =x.reduce(_ + _) x.print() y.print()
  29. 29. DataSet API GroupedDataset / DataSet: x DataSet: y val x = env.fromElements((“hola”,1), (“adiós”, 1), (“hola”, 3)) val y =x.groupBy(0).reduceGroup(elements => elements.length) x.print() y.print() REDUCEGROUP Devuelve un DataSet de un único elemento combinando todos los elementos del DataSet original mediante una función que recibe todos elementos en forma de iterable. Se puede aplicar en GroupedDataset, generando en este caso un DataSet con un elemento por cada clave. 1 2 ((“hola”,1), (“adiós”, 1), (“hola”, 3)
  30. 30. • Ejecutar y analizar el código el ejemplo de WordCount desde el IDE • Seguir los pasos de la slide “Preparar Entorno (2/2)” • Ejecutar el Main del archivo WordCount.java desde el IDE • Analizar clase LineSplitter  Lógica concreta de un FlatMap • ¿Cómo implementarías (sin usar la función SUM) la cuenta de palabras?  Lógica concreta de un ReduceGroupe Ejercicio 2 DataSet API
  31. 31. WordCount: FlatMap public static class LineSplitter implements FlatMapFunction<String, Tuple2<String, Integer>> { @Override public void flatMap(String value, Collector<Tuple2<String, Integer>> out){ // normalizer y partir la linea por palabras String[] tokens = value.toLowerCase().split("W+"); // emitir un par (palabra, 1) por cada palabra for (String token : tokens) { if (token.length() > 0) { out.collect(new Tuple2<String, Integer>(token, 1)); } } } }
  32. 32. WordCount: FlatMap: Interface public static class LineSplitter implements FlatMapFunction<String, Tuple2<String, Integer>> { @Override public void flatMap(String value, Collector<Tuple2<String, Integer>> out){ // normalizer y partir la linea por palabras String[] tokens = value.toLowerCase().split("W+"); // emitir un par (palabra, 1) por cada palabra for (String token : tokens) { if (token.length() > 0) { out.collect(new Tuple2<String, Integer>(token, 1)); } } } } 32
  33. 33. WordCount: FlatMap: tipos public static class LineSplitter implements FlatMapFunction<String, Tuple2<String, Integer>> { @Override public void flatMap(String value, Collector<Tuple2<String, Integer>> out){ // normalizar y partir la linea por palabras String[] tokens = value.toLowerCase().split("W+"); // emitir un par (palabra, 1) por cada palabra for (String token : tokens) { if (token.length() > 0) { out.collect(new Tuple2<String, Integer>(token, 1)); } } } } 33
  34. 34. WordCount: FlatMap: collector public static class LineSplitter implements FlatMapFunction<String, Tuple2<String, Integer>> { @Override public void flatMap(String value, Collector<Tuple2<String, Integer>> out){ // normalizer y partir la linea por palabras String[] tokens = value.toLowerCase().split("W+"); // emitir un par (palabra, 1) por cada palabra for (String token : tokens) { if (token.length() > 0) { out.collect(new Tuple2<String, Integer>(token, 1)); } } } }
  35. 35. WordCount: GroupReduce public static class SumWords implements GroupReduceFunction<Tuple2<String, Integer>, Tuple2<String, Integer>> { @Override public void reduce(Iterable<Tuple2<String, Integer>> values, Collector<Tuple2<String, Integer>> out) { int count = 0; String word = null; for (Tuple2<String, Integer> tuple : values) { word = tuple.f0; count++; } out.collect(new Tuple2<String, Integer>(word, count)); } }
  36. 36. WordCount: GroupReduce: Interface public static class SumWords implements GroupReduceFunction<Tuple2<String, Integer>, Tuple2<String, Integer>> { @Override public void reduce(Iterable<Tuple2<String, Integer>> values, Collector<Tuple2<String, Integer>> out) { int count = 0; String word = null; for (Tuple2<String, Integer> tuple : values) { word = tuple.f0; count++; } out.collect(new Tuple2<String, Integer>(word, count)); } }
  37. 37. WordCount: GroupReduce: tipos public static class SumWords implements GroupReduceFunction<Tuple2<String, Integer>, Tuple2<String, Integer>> { @Override public void reduce(Iterable<Tuple2<String, Integer>> values, Collector<Tuple2<String, Integer>> out) { int count = 0; String word = null; for (Tuple2<String, Integer> tuple : values) { word = tuple.f0; count++; } out.collect(new Tuple2<String, Integer>(word, count)); } }
  38. 38. WordCount: GroupReduce: collector public static class SumWords implements GroupReduceFunction<Tuple2<String, Integer>, Tuple2<String, Integer>> { @Override public void reduce(Iterable<Tuple2<String, Integer>> values, Collector<Tuple2<String, Integer>> out) { int count = 0; String word = null; for (Tuple2<String, Integer> tuple : values) { word = tuple.f0; count++; } out.collect(new Tuple2<String, Integer>(word, count)); } }
  39. 39. Desde Fichero • readTextFile(path, [TextInputFormat]): Lee línea a línea y las devuelve como String • readCsvFile(path, [CsvInputFormat ]):Lee línea a línea ficheros con campos separados por comas (u otro char). Genera un DataSet de tuplas o objetos case classes. • readHadoopFile(FileInputFormat, Key, Value, path ,[FileInputFormat ]) : Crea un JobConf y lee el fichero del path especificado usando el FileInputFormat. Se devuelven como Tuple2<Key, Value> Data Sources para DataSet
  40. 40. Desde Colecciones • fromCollection(Seq): Crear un DataSet a partir de un objeto Seq. Todos los elementos de la colección tienen que ser del mismo tipo. • fromCollection(Iterator): Crear un DataSet a partir de un objeto Iterator. El tipo de dato es el devuelto por el iterador. • fromElements(elements: _*): Crea un DataSet a partir de la secuencia de elementos introducidos directamente. Genéricos • readFile(inputFormat, path): Lee desde fichero pero con un format genérico que se especifica. • createInput(inputFormat) : Para crear un nuevo data Source (por ejemplo Kafka). Data Sources para DataSet
  41. 41. ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); // Leer desde fichero local o Sistema de ficheros distribuido DataSet<String> localLines = env.readTextFile(”/path/to/my/textfile"); // leer un CSV con 3 campos DataSet<Tuple3<Integer, String, Double>> csvInput = env.readCsvFile(“/the/CSV/file") .types(Integer.class, String.class, Double.class); // leer un CSV de 5 campos pero se queda solo con el primero y el cuarto DataSet<Tuple2<String, Double>> csvInput = env.readCsvFile(“/the/CSV/file") .ignoreFirstLine() .includeFields("10010") .types(String.class, Double.class); // leer un CSV y parsearlo como una clase POJO DataSet<Film> films = env.readCsvFile(“the/CSV/file“) .pojoType( Film.class, "name", "year", "nominations", "genre1"); Data Sources: ficheros
  42. 42. ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); // leer de elementos introducidos directamente DataSet<String> names = env.fromElements(“Some”, “Example”, “Strings”); // leer desde una colección List<String> list = new ArrayList<String>(); list.add(“Some”); list.add(“Example”); list.add(“Strings”); DataSet<String> names = env.fromCollection(list); Data Sources: colecciones
  43. 43. Text • writeAsText(“/path/to/file”) • writeAsCSV(“/path/to/file”, “;”) • output(outputFormat) CSV • writeAsCsv(“/path/to/file”) Devolver resultados al cliente • print() • collect() • count() Data Sinks para DataSet
  44. 44. // Reduce → Recibe 2 parámetros para aplicar de forma asociativa val text:DataSet[Tuple2[String, Int]] = env.fromElements(Tuple2("uno",1),Tuple2 ("dos",2),Tuple2 ("uno",11)) text.groupBy(0).reduce( (x, y) => (x._1, x._2 + y._2)).print() // ReduceGroup -> Recibe iterador como parámetro val text:DataSet[Tuple2[String, Int]] = env.fromElements(Tuple2("uno",1),Tuple2 ("dos",2),Tuple2 ("uno",11)) text.groupBy(0).reduceGroup(x => x.length).print() // Pasar funciones a las transformaciones val numeros:DataSet[Int] = env.fromElements(1,2,4,6,20) numeros.map(new MiMap).print() numeros.reduce(new MiReduce).print() class MiMap extends MapFunction[Int, Int] { def map(n: Int): Int = { return n*2 } } class MiReduce extends ReduceFunction[Int] { def reduce(a: Int, b:Int): Int = { return a+b } } // Parsear un Dataset con una case class case class Persona (id: Int, nombre: String) val d: DataSet[Persona] = env.fromElements(new Persona(1, “Rubén”) // usar un campo como clave d.groupBy(“nombre”).groupReduce(…) DataSet API en Scala
  45. 45. Transformación Descripción Map Dado un elemento, genera otro elemento data.map { x => x.toInt } FlatMap Dado un elemento produce 0, 1 o más elementos data.flatMap { str => str.split(" ") } MapPartition Recibe un iterador y produce un nº arbitrario de resultados. Se ejecuta por cada partición. data.mapPartition { in => in map { (_, 1) } } Filter Recibe un elemento y lo devuelve si la evaluación de la función es True sobre ese elemento data.filter { _ > 1000 } Reduce Combina elementos en uno único mediante una función asociativa. Se puede aplicar en GroupedDatasets data.reduce { _ + _ } ReduceGroup Reduce grupos de elementos en uno o varios elementos. data.reduceGroup { elements => elements.sum } Aggregate Agrega grupos de elementos en uno único. Se puede aplicar en DataSets y GroupedDasets val input: DataSet[(Int, String, Double)] = // [...] val output: DataSet[(Int, String, Doublr)] = input.aggregate(SUM, 0).aggregate(MIN, 2); Hay atajos para algunas funciones de agregación habituales val input: DataSet[(Int, String, Double)] = // [...] val output: DataSet[(Int, String, Doublr)] = input.sum(0).min(2) Transformaciones para DataSet
  46. 46. Transformación Descripción Distinct Devuelve los elementos distintos de un DataSet data.distinct() Join Une dos datasets creando todos los pares de elementos que son iguales en sus claves. Hay otras opciones de join con Join Hints // Los elementos son tuplas y se usa el campo 0 del Dataset 1 con el 1 del DataSet 2 val result = input1.join(input2).where(0).equalTo(1) OuterJoin Ejecutar left, right and full outer joins keys.val joined = left.leftOuterJoin(right).where(0).equalTo(1) { (left, right) => val a = if (left == null) "none" else left._1 (a, right) } Union Produce la union de dos DataSets. data.union(data2) Cross Producto cartestiano entre dos DataSets creando todos los pares de elementos. val data1: DataSet[Int] = // [...] val data2: DataSet[String] = // [...] val result: DataSet[(Int, String)] = data1.cross(data2) CoGroup Versión de 2 dimensiones del Reduce. Agrupa el input de uno o más campos y luego une los grupos. La función de transformación se aplica a los pares de grupos data1.coGroup(data2).where(0).equalTo(1).with( cogroupfunction) First-n Devuelve N elementos del DataSet. data1.first(3) Sort Partition Ordena una partición. val in: DataSet[(Int, String)] = // [...] val result = in.sortPartition(1, Order.ASCENDING).mapPartition { ... } Transformaciones para DataSet
  47. 47. • Usar el fichero “pictures.csv” https://cs.uwaterloo.ca/~s255khan/files/pictures.csv • Modelar cada película con una Case class / Pojo/ Tuple • Calcular con el API de DataSet • Media de nominaciones de las películas • Media en Metacritic, agrupado por géneros, de las películas • Duración media de las películas ganadoras por décadas • ¿Cuántas películas ganadoras incluyen al menos una de las palabras de su título en la sinopsis? • ¿Cuántas películas ganadoras incluyen todas las palabras de su título en la sinopsis? • ¿Cuál es la desviación estándar del rating de las películas ganadoras en el siglo XXI? name year nominations rating duration genre1 genre2 release metacritic synopsis Birdman 2014 9 7.8 119 Comedy Drama November 88 Illustrated upon the progress of his latest Broadway play a former popular actors struggle to cope with his current life as a wasted actor is shown. 12 Years a Slave 2013 9 8.1 134 Biography Drama November 97 In the antebellum United States Solomon Northup a free black man from upstate New York is abducted and sold into slavery. Ejercicio 3 DataSet API
  48. 48. 48 Gelly Table ML SAMOA DataSet (Java/Scala/Python) DataStream (Java/Scala) HadoopM/R Local Remote Yarn Tez Embedded Dataflow Dataflow(WiP) MRQL Table Cascading(WiP) Streaming dataflow runtime DataStream
  49. 49. Arquitectura de un sistema de streaming processing Capa de adquisición Cola de mensajes Capa de procesamiento Almacenamiento en memoria Capa de acceso Almacenamiento larga duración Origen
  50. 50. Capa de procesamiento: semántica  At most once: cada mensaje se procesa como máximo una vez. Se asegura que ningún mensaje es procesado más de una vez, pero podría pasar que algún mensaje no se procesase.  La más sencilla, no se requiere implementar ninguna lógica.  At least once: cada mensaje se procesa al menos una vez. Se asegura que todos los mensajes recibidos son procesados, pero podría pasar que algún mensaje se procesase más de una vez. • El sistema tiene que mantener un registro de los mensajes que se envió a la capa de procesamiento y enviar un ACK de recibimiento.  Exactly once: cada mensaje se procesa exactamente una vez. Ningún mensaje se queda sin procesar y ningún mensaje se procesa más de una vez. • La más compleja. El sistema debe mantener un registro de los mensajes enviados a la capa de procesamiento pero también detectar los duplicados.
  51. 51. Para un tiempo tn concreto, ¿depende la salida del sistema solo de la información recibida en tn (stateless) o también de la recibida en tn-1 (statefull)?  Ejemplos: • Lanzar una alarma cuando el PM10 sobrepase el valor de 25 • Identificar los trending topics en Twitter • Contar el nº de páginas vistas por hora por cada usuario  Los frameworks para streaming processing suelen disponer de APIs para gestionar estados  Flink lo tiene automático y manual Gestión del estadoEjemplo statefull Capa de procesamiento: estado
  52. 52.  Noción del tiempo • Event time: tiempo en el que el dato se generó • Stream/Ingestion time (system timestmap): tiempo en el que el dato entró en el sistema • Skew: diferencia entre el event time y el stream time Capa de procesamiento: eventos
  53. 53.  Ventana de datos (window): porción finita del streaming de datos sobre la que poder ejecutar los algoritmos. • Lo habitual es por tiempo, aunque podría ser por nº de elementos u otros mecanismos de disparo. • Existen diferentes tipos de ventanas: deslizante (sliding) y no deslizantes (tumbling) Capa de procesamiento: ventanas
  54. 54. Ventana deslizante  Sliding Window: técnica para el procesamiento de datos en continuo que divide el flujo en grupos finitos basándose en dos parámetros. • Longitud de la ventana (window length): Tiempo de datos que se tendrá en cuenta en el cálculo (desde tactual hasta tactual – longitud_ventana) • Intervalo: cada cuánto se recomputa el cálculo sobre los datos de la ventana  Ejemplo: Actualizar cada 1 segundo (intervalo) el valor de la mayor compra de los últimos 2 segundos (longitud de la ventana)
  55. 55. Ventana no deslizante  Tumbling window: técnica para el procesamiento de datos en continuo que divide el flujo en grupos finitos basándose solamente en la longitud de la ventana. • La longitud de la ventana puede ser por tiempo. Equivalente a ventana deslizante donde longitud_ventana = intervalo • La longitud de la ventana puede ser por nº de elementos. Hasta que no se reciba ese nº de elementos fijado no se produce ningún resultado.
  56. 56. DataStream • Lista distribuida de infinitos elementos  NO micro-batch • Se crea a partir de un StreamExecutionEnvironment o tras aplicar transformaciones a otro DataStream • Mismos conceptos de DataSource y Data Sinks • Las funciones de agregación solo tienen sentido sobre ventanas • Las funciones sobre Windows cuyo stream no es Clave-Valor (KeyedDataStream) NO son paralelas • Conceptos de ventanas (Tamaño y Trigger) Tumbling time window .timeWindow(Time.minutes(1)) Sliding time window .timeWindow(Time.minutes(1), Time.seconds(30)) Tumbling count window .countWindow(100) Sliding count window .countWindow(100, 10) Trigger .trigger(new miDisparador())
  57. 57. DataStream Heredan Generan
  58. 58. DataStream StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); String url = "wss://stream.meetup.com/2/rsvps"; DataStream<MeetupRSVGevent> events = env.addSource(new MeetupStreamingSource(url)); events.filter(new FilterNullsEvents()) .keyBy("venue") .timeWindow(Time.seconds(5)) .apply(new PojoCountPeople()).print(); env.execute("Meetup Madrid DataStream");
  59. 59. Data Sources para DataStream Desde fichero • readTextFile(path) • readFile(path) • readFileStream () Desde Socket • socketTextStream Desde Colección • fromCollection(Seq) • fromCollection(Iterator) • fromElements(elements: _*) Genérico • addSource ()  Para Kafka addSource(new FlinkKafkaConsumer09<>)
  60. 60. Data Sinks para DataStreams A fichero • writeAsText() • writeAsCsv(...) • writeUsingOutputFormat() Imprimir • print() • printToErr() A Socket • writeToSocket Genérico • addSink  para volcar a Kafka addSink(new FlinkKafkaConsumer09[](…)
  61. 61. Transformación Descripción Map DataStream → DataStream Dado un element genera otro dataStream.map { x => x * 2 } FlatMap DataStream → DataStream Dado un element genera 0,1 o más elementos dataStream.flatMap { str => str.split(" ") } Filter DataStream → DataStream Evalua una función boleana sobre el element y lo devuelve si es True dataStream.filter { _ != 0 } KeyBy DataStream → KeyedStream Particionado por clave para ejecutar operaciones en paralelo dataStream.keyBy("someKey") dataStream.keyBy(0) Reduce KeyedStream → DataStream Aplica la función asociativa con el anterior resultado y el nuevo elemento keyedStream.reduce { _ + _ } Fold KeyedStream → DataStream Combina el resultado anterior con el Nuevo element, utilizando un valor inicial val result: DataStream[String] = keyedStream.fold("start", (str, i) => { str + "-" + i }) Transformaciones para DataStream
  62. 62. Transformación Descripción Aggregations KeyedStream → DataStream Agregaciones. Min devuelve el mínimo valor y MinBy el elemento que contiene el mínimo valor keyedStream.sum(0) keyedStream.sum("key") keyedStream.min(0) keyedStream.min("key") keyedStream.max(0) keyedStream.max("key") keyedStream.minBy(0) keyedStream.minBy("key") keyedStream.maxBy(0) keyedStream.maxBy("key") Window KeyedStream → WindowedStream Agrupa los datos (en KeyedStream) según la definición de la ventana. dataStream.keyBy(0).window(TumblingEventTimeWindows.of(Ti me.seconds(5))) WindowAll DataStream → AllWindowedStream Lo mismo pero sobre DataStream (no KeyedStream). Puede no ser paralelo dataStream.windowAll(TumblingEventTimeWindows.of(Time.sec onds(5))) Apply WindowedStream → DataStream AllWindowedStream → DataStream Aplica una función sobre toda la ventana windowedStream.apply { WindowFunction} Transformaciones para DataStream
  63. 63. Transformación Descripción Window Reduce WindowedStream → DataStream Aplica una función Reduce sobre una ventana. windowedStream.reduce { _ + _ } Window Fold WindowedStream → DataStream Función de asociación usando un valor inicial sobre una ventana val result: DataStream[String] = windowedStream.fold("start", (str, i) => { str + "-" + i }) Aggregations on windows WindowedStream → DataStream Agregaciones sobre ventanas. Min devuelve el mínimo valor y MinBy el elemento que contiene el mínimo valor windowedStream.sum(0) windowedStream.sum("key") windowedStream.min(0) windowedStream.min("key") windowedStream.max(0) windowedStream.max("key") windowedStream.minBy(0) windowedStream.minBy("key") windowedStream.maxBy(0) windowedStream.maxBy("key") Transformaciones para DataStream
  64. 64. Transformation Description Union DataStream* → DataStream Unión de 2 o más DataStreams creando uno DataStream único con todos los elementos. dataStream.union(otherStream1, otherStream2, ...) Window Join DataStream,DataStream → DataStream Join two data streams on a given key and a common window. dataStream.join(otherStream) .where(0).equalTo(1) .window(TumblingEventTimeWindows.of(Time.seconds(3))) .apply { ... } Window CoGroup DataStream,DataStream → DataStream Cogroups two data streams on a given key and a common window. dataStream.coGroup(otherStream) .where(0).equalTo(1) .window(TumblingEventTimeWindows.of(Time.seconds(3))) .apply {} Extract Timestamps DataStream → DataStream Extraer el timpstmap de un registro para usar ventanas que trabajan con semántica de tiempos stream.assignTimestamps { timestampExtractor } Transformaciones para DataStream
  65. 65. • Descargar el proyecto esqueleto Meetup Madrid Flink e importarlo en el IDE https://onedrive.live.com/redir?resid=1F231B643ABC3A9C!11283& authkey=!AFsdm9PIjlkSrmM&ithint=file%2czip • Esqueleto para procesar datos en tiempo real de la red social Meetup. Se leen las respuestas RSVG • Analizar el código o La clase MeetupStreamingSource lee datos de un WebSocket y los añade como una fuente de datos a Flink (SourceFunction) o Se parsean los eventos (JSON) como un tipo de dato MeetupRSVGevent (Java) • Ejecutar el Main • Revisar la Web Admin (localhost:8081) Ejercicio 4a DataStream API
  66. 66. • Sobre el flujo de datos entrante: • Eliminar los objetos mal formados (alguno de sus campos a null) • Contar los usuarios que han confirmado a cada evento en los últimos 10 segundos • Contar los usuarios que han confirmado a cada evento en los últimos 20 segundos actualizando el resultado cada 5 segundos • Contar los usuarios por países cada 5 segundos • Calcular los Trending Topics (palabras semánticamente significativas más repetidos de los topic_name) teniendo en cuenta la información del último minuto y actualizando el resultado cada 10 segundos Ejercicio 4b DataStream API
  67. 67. ¡GRACIAS! @ruben_casado ruben.casado@outlook.com

×