Super detallado! ¡Un artículo que explica en detalle cómo SparkStreaming integra Kafka! El código adjunto se puede practicar

Fuente | Alice

Editor en jefe | Carol

封 图 | CSDN Descargar en Visual China

Venta | CSDN (ID: CSDNnews)

Creo que muchos socios pequeños ya se han puesto en contacto con SparkStreaming, por lo que no hablaré demasiado sobre la teoría. El contenido de hoy es principalmente para brindarle un tutorial sobre la integración de SparkStreaming con Kafka.

El código está incluido en el texto, y los amigos interesados ​​pueden copiarlo y probarlo.

Revisión de Kafka

Antes del inicio oficial, revisemos Kafka.

  • Ilustración de conceptos básicos

Broker: la máquina donde está instalado el servicio Kafka es un broker

Productor: Productor del mensaje, responsable de escribir datos en el corredor (push)

Consumidor: el consumidor de mensajes, responsable de extraer datos de kafka (pull), la versión anterior del consumidor debe confiar en zk, la nueva versión no necesita

Tema: El  tema es equivalente a una clasificación de datos. Diferentes temas almacenan datos para diferentes negocios  - Tema: Distinguir negocios

Replicación: copia, cuántas copias de datos se guardan (para garantizar que los datos no se pierdan)  - copia: seguridad de datos

Partición: Partición, es una partición física, una partición es un archivo, un tema puede tener 1 ~ n particiones, cada partición tiene su propia copia  -partición: lectura y escritura concurrentes

Grupo de consumidores: un grupo de consumidores, un tema puede tener múltiples consumidores / grupos consumiendo al mismo tiempo, si varios consumidores están en un grupo de consumidores, entonces no pueden consumir datos repetidamente  - grupo de consumidores: mejorar la velocidad y conveniencia del consumo Gestión unificada

Nota [1]: un tema puede ser suscrito por múltiples consumidores o grupos, y un consumidor / grupo también puede suscribirse a múltiples temas

Nota [2]: Los datos de lectura solo se pueden leer del líder, y los datos de escritura solo se pueden escribir en el líder. ¡El seguidor sincronizará los datos del líder para hacer una copia! ! !

  • Comandos comunes

Comience kafka

/export/servers/kafka/bin/kafka-server-start.sh -daemon 

/export/servers/kafka/config/server.properties 

Deja de kafka

/export/servers/kafka/bin/kafka-server-stop.sh 

Ver información del tema

/export/servers/kafka/bin/kafka-topics.sh --list --zookeeper node01:2181

Crear tema

/export/servers/kafka/bin/kafka-topics.sh --create --zookeeper node01:2181 --replication-factor 3 --partitions 3 --topic test

Ver información sobre un tema

/export/servers/kafka/bin/kafka-topics.sh --describe --zookeeper node01:2181 --topic test

Eliminar tema

/export/servers/kafka/bin/kafka-topics.sh --zookeeper node01:2181 --delete --topic test

Start Producer: el productor de la consola generalmente se usa para pruebas

/export/servers/kafka/bin/kafka-console-producer.sh --broker-list node01:9092 --topic spark_kafka

Consumidor inicial: el consumidor de la consola generalmente se usa para pruebas

/export/servers/kafka/bin/kafka-console-consumer.sh --zookeeper node01:2181 --topic spark_kafka--from-beginning

Dirección del consumidor para conectarse a Borker

/export/servers/kafka/bin/kafka-console-consumer.sh --bootstrap-server node01:9092,node02:9092,node03:9092 --topic spark_kafka --from-beginning 



Descripción de los dos modos de integración de Kafka

Este también es un tema candente de las preguntas de la entrevista.

En el desarrollo, a menudo utilizamos SparkStreaming para leer datos en kafka en tiempo real y luego procesarlos. Después de la versión spark1.3, kafkaUtils proporciona dos métodos para crear DStream:

1. Método de recepción del receptor:

  • KafkaUtils.createDstream (no se usa en desarrollo, solo entiendo, pero la entrevista puede preguntar).

  • Receiver se ejecuta como una Tarea residente en el Ejecutor para esperar los datos, pero un Receiver es ineficiente, debe abrir múltiples y luego combinar manualmente los datos (unión), y luego procesarlos, es muy problemático

  • ¿Qué máquina de Receiver se cuelga puede perder datos, por lo que debe habilitar WAL (registro previo a la escritura) para garantizar la seguridad de los datos, entonces se reducirá la eficiencia!

  • El método de Receiver es conectar la cola kafka a través de zookeeper, llamar a la API de alto nivel de Kafka y el desplazamiento se almacena en zookeeper, que es mantenido por Receiver.

  • Para garantizar que los datos no se pierdan, la chispa también guardará un desplazamiento en el punto de control durante el consumo, y puede producirse una inconsistencia de datos

  • Entonces, no importa desde qué ángulo, el modo Receptor no es adecuado para su uso en el desarrollo, se ha eliminado

2. Conexión directa

  • KafkaUtils.createDirectStream (usado en desarrollo, requiere dominio)

  • El método directo es conectarse directamente a la partición Kafka para obtener datos. Leer datos directamente de cada partición mejora enormemente el paralelismo.

  • Llame directamente a Kafka API de bajo nivel (API de nivel inferior), el desplazamiento se almacena y se mantiene de forma predeterminada, Spark se mantiene en el punto de control de forma predeterminada, eliminando la inconsistencia con zk

  • Por supuesto, también puede mantenerlo manualmente y almacenar el desplazamiento en mysql y redis.

  • Por lo tanto, se puede usar en el desarrollo basado en el modo directo, y con la ayuda de las características del modo directo + operación manual, los datos se pueden garantizar exactamente una vez

Resumen:

  • Método de recepción del receptor

  1. Varios receptores aceptan datos con alta eficiencia, pero corren el riesgo de perderlos.

  2. Activar el registro (WAL) puede evitar la pérdida de datos, pero es ineficiente escribir datos dos veces.

  3. Zookeeper mantiene un desplazamiento y puede consumir datos repetidamente.

  4. Use una API de alto nivel

  • Conexión directa

  1. Lea los datos directamente en la partición Kafka sin usar Receiver

  2. No utiliza el mecanismo de registro (WAL)

  3. Spark mantiene el desplazamiento en sí

  4. Usar API de bajo nivel

Extensión: con respecto a la semántica de mensajes
:

Hay dos versiones de SparkStreaming y kafka integradas en desarrollo: 0.8 y 0.10+

La versión 0.8 tiene modos Receiver y Direct (pero la versión 0.8 tiene más problemas de entorno de producción, y la versión 0.8 no es compatible después de Spark2.3).

Después de 0.10, solo se ha retenido el modo directo (el modo Reveiver no es adecuado para entornos de producción), y la versión 0.10 API ha cambiado (más potente)

Conclusión

Estudiamos y desarrollamos directamente usando el modo directo en la versión 0.10, pero la entrevista sobre la diferencia entre Receiver y Direct debería poder responder.

spark-streaming-kafka-0-8 (entiendo)

1 receptor

KafkaUtils.createDstream utiliza receptores para recibir datos, utilizando la API de consumo de alto nivel de Kafka, Receiver mantiene el desplazamiento en zk, y los datos recibidos por todos los receptores se guardarán en los ejecutores de Spark, y luego a través de Spark La transmisión inicia un trabajo para procesar estos datos, que se perderán de forma predeterminada. El registro WAL se puede habilitar. Sincroniza y guarda los datos recibidos en un sistema de archivos distribuido como HDFS. Asegúrese de que los datos se puedan recuperar en caso de error. Si bien este método, combinado con el mecanismo WAL, puede garantizar una alta confiabilidad de pérdida de datos cero, la eficiencia de WAL es menor y no hay garantía de que los datos se procesen una sola vez y solo una vez, y pueden procesarse dos veces. Porque Spark y ZooKeeper pueden no estar sincronizados.

(Los funcionarios no recomiendan este tipo de integración ahora).

  • Preparación

1) Inicie el clúster zookeeper

zkServer.sh start

2) Inicie el clúster kafka

kafka-server-start.sh  /export/servers/kafka/config/server.properties

3. Crear tema

kafka-topics.sh --create --zookeeper node01:2181 --replication-factor 1 --partitions 3 --topic spark_kafka

4. Enviar mensaje al tema a través del comando de shell

kafka-console-producer.sh --broker-list node01:9092 --topic  spark_kafka

5. Agregar la dependencia pom de kafka

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
    <version>2.2.0</version>
</dependency>
  • API

Obtenga los datos del tema en kafka a través del receptor, puede ejecutar más receptores para leer los datos en el tema kafak en paralelo, aquí hay 3

 val receiverDStream: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => {
      val stream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics)
      stream
    })

Si WAL está habilitado (spark.streaming.receiver.writeAheadLog.enable = true), se puede establecer el nivel de almacenamiento (predeterminado StorageLevel.MEMORY_AND_DISK_SER_2)

Código de demostración

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.immutable

object SparkKafka {
  def main(args: Array[String]): Unit = {
    //1.创建StreamingContext
    val config: SparkConf = 
new SparkConf().setAppName("SparkStream").setMaster("local[*]")
      .set("spark.streaming.receiver.writeAheadLog.enable", "true")
//开启WAL预写日志,保证数据源端可靠性
    val sc = new SparkContext(config)
    sc.setLogLevel("WARN")
    val ssc = new StreamingContext(sc,Seconds(5))
    ssc.checkpoint("./kafka")
//==============================================
    //2.准备配置参数
    val zkQuorum = "node01:2181,node02:2181,node03:2181"
    val groupId = "spark"
    val topics = Map("spark_kafka" -> 2)//2表示每一个topic对应分区都采用2个线程去消费,
//ssc的rdd分区和kafka的topic分区不一样,增加消费线程数,并不增加spark的并行处理数据数量
    //3.通过receiver接收器获取kafka中topic数据,可以并行运行更多的接收器读取kafak topic中的数据,这里为3个
    val receiverDStream: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => {
      val stream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics)
      stream
    })
    //4.使用union方法,将所有receiver接受器产生的Dstream进行合并
    val allDStream: DStream[(String, String)] = ssc.union(receiverDStream)
    //5.获取topic的数据(String, String) 第1个String表示topic的名称,第2个String表示topic的数据
    val data: DStream[String] = allDStream.map(_._2)
//==============================================
    //6.WordCount
    val words: DStream[String] = data.flatMap(_.split(" "))
    val wordAndOne: DStream[(String, Int)] = words.map((_, 1))
    val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_ + _)
    result.print()
    ssc.start()
    ssc.awaitTermination()
  }
}

2 directo

El método directo consultará periódicamente el último desplazamiento de la partición correspondiente bajo el tema de kafka, y luego procesará los datos en cada lote de acuerdo con el rango de desplazamiento. .

  • La desventaja de Direct es que no puede usar la herramienta de monitoreo kafka basada en zookeeper

  • Direct tiene varias ventajas sobre Receiver:

  1. Simplifica el paralelismo

    No es necesario crear múltiples flujos de entrada kafka y luego unirlos. SparkStreaming creará el mismo número de particiones RDD que las particiones Kafka, y leerá los datos en paralelo de Kafka, el número de particiones RDD en Spark y las particiones en Kafka Los datos son una relación uno a uno.

  2. Eficiente 

    La pérdida cero de datos lograda por Receiver es guardar los datos en el WAL de antemano. Los datos se copiarán una vez, lo que hará que los datos se copien dos veces, la primera vez que Kafka los copia, y la otra vez se escribe en el WAL. Direct no usa WAL para eliminar este problema.

  3. Exactamente una vez semántica

    El receptor lee los datos de kafka a través de la API de alto nivel de kafka para escribir el desplazamiento en zookeeper. Aunque este método puede guardar los datos en el WAL para garantizar que no se pierdan, puede deberse a que el desplazamiento almacenado en sparkStreaming y ZK es inconsistente Como resultado, los datos se consumieron muchas veces.

        Direct's Exactly-once-semántica (EOS) implementa la API kafka de bajo nivel, y el desplazamiento solo se guarda mediante el ssc en el punto de control, eliminando el problema de inconsistencias entre los desplazamientos zk y ssc.

  • API

KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topics)

Código de demostración

import kafka.serializer.StringDecoder
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}


object SparkKafka2 {
  def main(args: Array[String]): Unit = {
    //1.创建StreamingContext
    val config: SparkConf = 
new SparkConf().setAppName("SparkStream").setMaster("local[*]")
    val sc = new SparkContext(config)
    sc.setLogLevel("WARN")
    val ssc = new StreamingContext(sc,Seconds(5))
    ssc.checkpoint("./kafka")
    //==============================================
    //2.准备配置参数
    val kafkaParams = Map("metadata.broker.list" -> "node01:9092,node02:9092,node03:9092", "group.id" -> "spark")
    val topics = Set("spark_kafka")
    val allDStream: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topics)
    //3.获取topic的数据
    val data: DStream[String] = allDStream.map(_._2)
    //==============================================
    //WordCount
    val words: DStream[String] = data.flatMap(_.split(" "))
    val wordAndOne: DStream[(String, Int)] = words.map((_, 1))
    val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_ + _)
    result.print()
    ssc.start()
    ssc.awaitTermination()
  }
}


spark-streaming-kafka-0-10

  • Explicación

En la versión spark-streaming-kafka-0-10, la API tiene algunos cambios, la operación es más flexible y se usa en el desarrollo

  • pom.xml

<!--<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
    <version>${spark.version}</version>
</dependency>-->
<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
    <version>${spark.version}</version>
</dependency>
  • API :

http://spark.apache.org/docs/latest/streaming-kafka-0-10-integration.html

  • Crear tema

/export/servers/kafka/bin/kafka-topics.sh --create --zookeeper node01:2181 --replication-factor 3 --partitions 3 --topic spark_kafka

  • Iniciar productor

/export/servers/kafka/bin/kafka-console-producer.sh --broker-list node01:9092,node01:9092,node01:9092 --topic spark_kafka

  • Código de demostración

import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

object SparkKafkaDemo {
  def main(args: Array[String]): Unit = {
    //1.创建StreamingContext
    //spark.master should be set as local[n], n > 1
    val conf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")
    val ssc = new StreamingContext(sc,Seconds(5))//5表示5秒中对数据进行切分形成一个RDD
    //准备连接Kafka的参数
    val kafkaParams = Map[String, Object](
      "bootstrap.servers" -> "node01:9092,node02:9092,node03:9092",
      "key.deserializer" -> classOf[StringDeserializer],
      "value.deserializer" -> classOf[StringDeserializer],
      "group.id" -> "SparkKafkaDemo",
      //earliest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
      //latest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
      //none:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
      //这里配置latest自动重置偏移量为最新的偏移量,即如果有偏移量从偏移量位置开始消费,没有偏移量从新来的数据开始消费
      "auto.offset.reset" -> "latest",
      //false表示关闭自动提交.由spark帮你提交到Checkpoint或程序员手动维护
      "enable.auto.commit" -> (false: java.lang.Boolean)
    )
    val topics = Array("spark_kafka")
    //2.使用KafkaUtil连接Kafak获取数据
    val recordDStream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String](ssc,
      LocationStrategies.PreferConsistent,//位置策略,源码强烈推荐使用该策略,会让Spark的Executor和Kafka的Broker均匀对应
      ConsumerStrategies.Subscribe[String, String](topics, kafkaParams))//消费策略,源码强烈推荐使用该策略
    //3.获取VALUE数据
    val lineDStream: DStream[String] = recordDStream.map(_.value())//_指的是ConsumerRecord
    val wrodDStream: DStream[String] = lineDStream.flatMap(_.split(" ")) //_指的是发过来的value,即一行数据
    val wordAndOneDStream: DStream[(String, Int)] = wrodDStream.map((_,1))
    val result: DStream[(String, Int)] = wordAndOneDStream.reduceByKey(_+_)
    result.print()
    ssc.start()//开启
    ssc.awaitTermination()//等待优雅停止
  }
}

Ok, este artículo explica principalmente el proceso de integración de SparkStreaming con Kafka y te lleva a revisar los conocimientos básicos de Kafka. Si te resulta útil, intenta "mirar" ~

Este artículo fue publicado por primera vez por el autor en el blog CSDN, el enlace original:

https://blog.csdn.net/weixin_44318830/article/details/105612516

【FIN】

Más recomendaciones interesantes

30 años de emoción de código abierto: de una comunidad libre a una compañía multimillonaria

☞Entender uno de los mayores logros de AI: las limitaciones de las redes neuronales convolucionales

GitHub star 10,000+, el camino de código abierto del principal proyecto de Apache ShardingSphere

El académico Zheng Guangting de la Universidad de Ciencia y Tecnología de Hong Kong pregunta sobre el futuro y expone la última aplicación y práctica de IA

Challenge Desafío inteligente de O&M bajo una gran promoción: ¿Cómo puede Ali resistirse a la "Noche de gatos doble 11"?

Plaza Ethernet 2.0 Custodia del juego y poner en práctica MPC

He escrito 9 preguntas de entrevista MySQL para usted con mucho cuidado.

Cada "observación" que pides, me lo tomo en serio

Artículos originales publicados en 1984 · Más de 40,000 elogios · 18.44 millones de visitas

Supongo que te gusta

Origin blog.csdn.net/csdnnews/article/details/105697457
Recomendado
Clasificación